为什么这段代码需要8分钟才能完成?

时间:2013-03-18 00:00:15

标签: ruby

对于其中一个项目Euler问题,这是一个(非常糟糕的)解决方案。问题是找到10_001st素数。下面的代码可以实现,但运行需要8分钟。你能解释为什么会这样,以及如何优化它?

primes = []
number = 2.0

until primes[10000] != nil
  if (2..(number - 1)).any? do |n|
    number % n == 0
  end == false
    primes << number
  end
  number = number + 1.0
end

puts primes[10000]

2 个答案:

答案 0 :(得分:7)

主要发现的一些简单优化:

  • 首先将2推入素数列表,然后检查3是否为素数。 (这消除了为数字0到2编写特殊情况代码的需要)

  • 您只需要检查主要候选资格的奇数。 (或者,如果你开始添加2/3/5并检查7,你只需要在执行%6后检查1或5的数字。或者......你明白了)

  • 您只需要看看您当前的候选人x是否可以被截至sqrt(x)的因子整除 - 因为任何高于sqrt(x)的因子将x除以下面的数字sqrt(x),您已经检查了所有这些内容。

  • 您只需检查主要列表中的数字,而不是所有数字,对于x的除数 - 因为所有复合数字都可以被素数整除。例如,81是9 * 9 - 但9 * 9是3 * 3 * 9,9是复合的,所以当你检查它时你会发现它是一个素数。因此你永远不需要测试9是否是一个因素等每个复合因素。

有非常优化的,加速的主要查找功能(请参阅Sieve of Atkin开始),但这些是很容易想到的常见优化。

答案 1 :(得分:1)

你真的要检查这个数字是否除以以前的所有数字?只检查您已发现的较小素数。另外,为什么使用整数完全正常的浮点数?

修改

一些可能的变化(不是最好的算法,可以改进):

primes = [2, 3, 5]
num = 7

until primes[10000]
  is_prime = true
  i = 0
  sqrtnum = Math.sqrt(num).ceil
  while (n=primes[i+=1]) <= sqrtnum
    if num % n == 0
      is_prime = false
      break
    end
  end
  if is_prime
    primes << num
  end
  num += 2
end

puts primes[10000]

在我的电脑上(1000个素数):

 Yours:
real    0m3.300s
user    0m3.284s
sys     0m0.000s

 Mine:
real    0m0.045s
user    0m0.040s
sys     0m0.004s