【问题标题】:Partial Fibonacci Sum, how to improve performance?部分斐波那契和,如何提高性能?
【发布时间】:2016-09-06 20:41:34
【问题描述】:

我需要改进这个算法的性能。我相信答案在于皮萨诺时期的应用。

此算法必须返回从 f(m) 到 f(n) 的 fib 数之和的最后一位。

这是我目前所拥有的:

def fib(n)
    a = []
    a << 0 << 1
    (n+1).times do |i|
        a << a[-1] + a[-2]
    end
     a[n]
end


def fib_partial_sum(m, n)
    if n == m
        fib(m) % 10
    else
        m = fib(m + 1) - 1
        n = fib(n + 2) - 1
        (n - m) % 10
    end
end

if __FILE__ == $0
  m, n = gets.split().map(&:to_i)
  puts "#{fib_partial_sum(m, n)}"
end

任何 fib num 的最后一位数字每 60 个数字重复一次。因此,我们可以这样做,n, m = n % 60, m % 60。在输入 567717153638 567717153638 上进行了改进,但还没有完全实现:

def fib(n)
    a = []
    a << 0 << 1
    (n+1).times do |i|
        a << a[-1] + a[-2]
    end
     a[n]
end

def fib_partial_sum(m, n)
    if n == m
        fib(m)
    else
        m = m % 60
        n = n % 60

        m = fib(m + 1) - 1
        n = fib(n + 2) - 1
        (n - m) % 10
    end
end

if __FILE__ == $0
  m, n = gets.split().map(&:to_i)
  puts "#{fib_partial_sum(m, n)}"
end

【问题讨论】:

  • 这应该在code review 上吗?除非它是不工作的代码。标题表明确实如此,但质疑性能。
  • 顺便问一下,你有理由在fib 上使用数组吗?您的函数基本上返回一个数字;您可以使用两个临时变量而不是数组轻松地做同样的事情(它具有分配/重新分配和垃圾收集的开销以及更大的内存占用)。
  • 斐波那契数的最后一位数字每六十个数字重复一次,因此您可以将 n 和 m 替换为 n % 60 和 m % 60。(您实际上可以只使用最终数字序列的数组,但是您可能正在尝试学习算法技术,所以这可能不是您想要的。)
  • @matthewalexander 该输入失败,因为您仅更改了fib_partial_sum 中的else,在本例中为n = m。将if 正文更改为fib(m % 60),它应该可以工作。
  • "Any insight is greatly appreciated." 也是不可取的。虽然很高兴表达对帮助的感谢,但作为参考书而不是讨论板,SO 有更正式的立场。把它想象成一本教科书或食谱,并据此编写。一些熟悉和友好是受欢迎的,但签名、问候和感谢不会被删掉。

标签: ruby fibonacci


【解决方案1】:

这是一个很好的问题解决方案,它通过了所有时间和内存限制。 这样,您永远不必计算大于 f(60) 的 fib num。它可以有效地处理非常大的输入。

def fib(n)
    a, b = 0, 1
    (n-1).times do
        a, b = b, (a + b) % 10
    end
    b
end

def fib_partial_sum(m, n)
    if n == m
        fib(m % 60)
    else
        m = m % 60
        n = n % 60

        m = fib(m + 1) - 1
        n = fib(n + 2) - 1

        (n - m) % 10
    end
end

if __FILE__ == $0
    m, n = gets.split().map(&:to_i)
    puts "#{fib_partial_sum(m, n)}"
end

(使用的最大时间:0.05/5.00,使用的最大内存:8699904/536870912。)

【讨论】:

    【解决方案2】:

    以下内容只需要一次传递零到最多 [n,m+60].min 之间的数字,其中 m..n 是感兴趣的范围,并且具有最小的内存要求。它利用了@nloveladyallen 的观察,即斐波那契数的最后一位数字的周期为 60。

    代码

    def fib_last(m,n)
      n -= 60*((n-m)/60)   
      fib_sum(m,n) % 10
    end
    
    def fib_sum(m,n)
      return nil if m < 0 || m > n
      return n if n < 2
      next_to_last, last = fib(m-1)
      (m..n).reduce(0) do |tot,_|
        current = next_to_last + last
        next_to_last = last
        last = current
        tot + current
      end
    end
    
    def fib(m)
      next_to_last, last = -1, 1
      0.upto(m).each do |n|
        current = next_to_last + last
        next_to_last, last = last, current
      end
      [next_to_last, last]
    end
    

    示例

    m, n = 6, 12
    (n+1).times { |i| puts "#{i}: #{fib(i)}" }
    0:  [0,    0]
    1:  [0,    1]
    2:  [1,    1]
    3:  [1,    2]
    4:  [2,    3]
    5:  [3,    5]
    6:  [5,    8]
    7:  [8,   13]
    8:  [13,  21]
    9:  [21,  34]
    10: [34,  55]
    11: [55,  89]
    12: [89, 144]
    
    fib_last(6,12) #=> 4 (fib_sum(6,12) #=> 8 + 13 + 21 + 34 + 55 + 89 + 144 = 364)
    fib_last(1,2)  #=> 2 (fib_sum(1,2)  #=> 1 + 1 = 2)
    fib_last(1,3)  #=> 4 (fib_sum(1,3)  #=> 1 + 1 + 2 = 4) 
    fib_last(1,4)  #=> 7 (fib_sum(1,4)  #=> 1 + 1 + 2 + 3 = 7)
    fib_last(2,3)  #=> 3 (fib_sum(2,3)  #=> 1 + 2 = 3)
    

    【讨论】:

    • 输入 1、2 失败
    • 输入 1、3 失败
    • 嗨 Cary - 非常感谢您的努力,请查看我的最新更新,了解我所追求的解决方案。我有严格的时间和内存限制。
    • 我已经修改了我的代码,加入了@nloveladyallen 对斐波那契数的最后一位数字的周期性的观察。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2013-09-29
    • 2020-07-24
    • 2016-03-07
    • 2021-09-18
    • 2011-02-16
    • 1970-01-01
    • 2012-12-29
    相关资源
    最近更新 更多