如何修复中止Fibonacci序列码

时间:2017-03-15 18:51:40

标签: ruby-on-rails arrays ruby memory-leaks fibonacci

我试图得到一个包含500万个元素的Fibonacci序列。

当我将1000作为参数传递时,此代码异常中止。

def self.fibo_seq(limit)
  result_array = [0,1]
  return result_array if limit < 2
   while result_array.length <= limit
     result_array << result_array[-1] + result_array[-2]
   end
  return result_array
end
res= Multiple.fibo_seq(5_000_000)
print res

Error: [1]    22382 killed     ruby fibo.rb

示例输出:

# >> [0, 1, 1, 2, 3, 5, 8, 13, 21, 34, , 1...] upto 5 Million elements

4 个答案:

答案 0 :(得分:4)

使用YARV的Integer实现存储前5000000个Fibonacci数字在64位平台上使用1084762047712字节(假设每字节8位)。这接近一个TiByte(准确地说是0.9865853351 TiByte)。这只是数字本身的空间,还有数组的开销(几个字节)和数组内的指针(稍微少于5000000次8,或者一点点)超过38 MiByte)。

计算那5000000个数字,即使没有存储它们(只记得最后2个以避免重新计算),在我2011年末的MacBook Pro上花了20多分钟。在计算它们的同时分配1个TiByte的RAM将会慢得多。如果你没有1个TiByte的RAM,并且操作系统开始交换到磁盘,那么即使你有一个通过FibreChannel连接的超强RAID固态硬盘,它也会慢一个数量级。

为了打印数组,首先需要将其表示为字符串。即使只是逗号和空格没有这些数字已经是4999999 * 2个字符,需要接近10 MiByte的RAM(假设是单字节字符集)。如果您尝试仅打印逗号和空格,则需要大约2500页的DIN A4纸,如果双面打印则需要1250页。办公用纸通常以500页的堆叠形式出售,大约5厘米高,所以你有2.5层高约12.5厘米只用于逗号和空格

5000000数字的总位数,以及字符(和字节)大约是2.7万亿位数,对于要打印的最终字符串,大约有2.5 TiByte的RAM。在DIN A4双面打印会产生33公里高的纸叠,是山的高度的4倍。珠穆朗玛峰。

总而言之,在您调用print时,您的程序需要大约3.5 TiByte的RAM。

打印到控制台的速度实际上非常慢,在我的标准macOS Terminal.app上,我得到大约1 MiByte / s,这意味着计算5000000个数字不仅需要至少几十分钟,而且还不计算时间分配所有这些对象和所有RAM,不仅你的程序使用3.5 TiByte的RAM,只需在终端上显示最终数组的行为将花费大约一个月

tl; dr summary :5000000斐波那契数字

答案 1 :(得分:2)

此程序的问题可能是内存限制。但你真的需要所有这些数字吗?如果是,那么你最好获得更多的硬件。

否则,如果您需要序列中的第五百万个数字,您可以通过仅存储最后两个数字来大大加快您的程序。

改进的最后一步:在常数时间内计算斐波纳契序列的任意成员! - &#34; Find The Millionth Fibonacci in Java&#34;

答案 2 :(得分:1)

由于所涉及的记忆和时间,生成5M的Fibonacci序列是一个问题。

生成后,下一个问题就会重新使用这些结果,因此您不必再进行两次。即使它适合,将序列存储在内存中也是愚蠢的,因为代码或机器崩溃将强制重新生成值,如果你需要5,000,000,你可以等待很长时间才能准备好应用程序做一些有用的事情,所以把它们放进去吧在磁盘上,在平面文件中,或在数据库中,您可以相对快速地检索所需的特定值。

这是生成平面文件的简单代码,在我感到无聊并停止之前,我测试了多达25,000个文件。它似乎对于那个测试做得很好,但我想它会随着Ruby改变现状而放慢速度。我不知道上限是什么,缺乏耐心去发现。

limit = ARGV.shift.to_i

puts "#{limit} iterations"

File.open('fibonacci.out', 'w') do |fo|
  ary = [0, 1]
  fo.puts ary
  break if limit < 2

  (limit - ary.length).times do |i|
    next_nbr = ary[-1] + ary[-2]
    ary.shift
    ary.push(next_nbr) 

    fo.puts next_nbr
    print 2 + i, "\r"
  end

  puts
end

你可以通过摆脱ary获得一点速度。

使用

运行它
ruby test.rb 5

导致“fibonacci.out”包含:

0
1
1
2
3

这似乎是正确的。

对于数据库有Fibonacci生成器,但如果它们是递归的,那么最终会在尝试生成大数字时取出DBM,因此使用简单的生成器然后将值存储到表中似乎更合理。

答案 3 :(得分:0)

这可能比替代方案更好。第500万个斐波纳契数字将有大约一百万个数字。忽略计算它的时间,存储所有这些将占用1TB的内存和至少另外2TB的内存或存储器用于输出。

最重要的是,如果你想这样做,你不能在普通的台式机上做,也不应该用Ruby做。

对于那些问我如何获得号码的人:

根据维基百科https://en.wikipedia.org/wiki/Fibonacci_number#Magnitude,数字的位数大约是n的0.2090倍,因此对于百万分之五的数字,大约是一百万位。我没有密切关注Ruby的BigNumber实现,但我假设每个字节有2位数,这是十进制算术的最简单表示。您可以打包几个位(10位中的3位数而不是8位中的2位数)但是这里的结果不会更改。

对于整个数组,我只使用了算术系列之和的标准公式。 File -> Settings -> "Appearance & Behavior" -> "System Settings" -> "Android SDK" -> "SDK Tools" -> Check mark "Show Package Details" :5,000,000 / 2 * 1,000,000或2.5e12位。每字节2位数,这将是大约1TB的内部存储器(不计算Ruby添加其内部结构和间接的开销)。

如果你打印或存储它,你可以指望每个字节1位数(UTF-8),这样就需要2.5TB,不计4,999,999个逗号。