加速Ruby中最强大的素因子程序

时间:2016-09-05 23:55:00

标签: ruby algorithm performance primes

一个简单的程序,用于在Ruby中找到数字的最大素数因子,由2种方法组成:

def is_prime?(n)
  (2..n).select {|number| n % number == 0}.length == 1 ? true : false
end

def prime_factors(number)
  (1..number).select {|m| number % m == 0 && is_prime?(m) == true}.max
end

对于像100这样的小数字它可以正常工作。但是,我试图解决Project Euler上的问题,它使用数字600851475143.当尝试这个时,问题甚至不能运行,我最终取消了它大约一分钟。

如何更改此功能以提高性能?

3 个答案:

答案 0 :(得分:5)

使用Ruby的Prime#prime_division方法:

require 'prime'

Prime.prime_division(600851475143).max_by(&:first).first
  #=> 6857

三个步骤:

a = Prime.prime_division(600851475143)
  #=> [[71, 1], [839, 1], [1471, 1], [6857, 1]]
  # Note (71**1)*(839**1)*(1471**1)*(6857**1) #=> 600851475143  
b = a.max_by(&:first)
  #=> [6857, 1] 
b.first
  #=> 6857 

答案 1 :(得分:3)

有一种分解方法,它更有效,而且只能找到素因子,无需测试素数。跳到最后看到它或阅读以了解我是如何到达那里的。

我重构了你的解决方案,以便更容易思考,提取factors方法,删除不必要的布尔值显式用法并重命名一些东西:

def greatest_prime_factor(n)
  factors(n).select { |c| prime?(c) }.max
end

def factors(n)
  (1..n).select { |c| n % c == 0 }
end

def prime?(n)
  (2..n).count { |c| n % c == 0 } == 1
end

factors效率低下:即使我们知道数字c(对于“候选人”)是一个因素,我们也会测试大于n / c的数字。为了解决这个问题,当我们找到一个因子时,在我们寻找下一个因素之前将n除以它:

def factors(n)
  if n == 1
    []
  else
    f = (2..n).find { |c| n % c == 0 }
    [f] + factors(n / f)
  end
end

(其他方法保持不变。)通过该更改,整个程序在几毫秒内计算600851475143(不计算Ruby进程启动时间)。

为什么这种改变如此有效? factors不仅缓慢;原始程序在prime?中花费了大约75%的时间。但是,虽然factors的原始版本返回了所有数量的因素,包括素数和非素数,但factors的新版本仅返回素数。 (这是因为它累积了最小的剩余因子,最小的素因子总是小于最小的非主要因素。)因此,我们现在不仅要更快地考虑因素,我们还有更少的因素来测试素数,以及我们的因素。对初始性的测试较小,并且花费较少的时间来测试素数。

更重要的是,由于factors现在返回素数,我们根本不需要测试素数,所以让我们重命名factors以明确它返回素数并消除prime?

def greatest_prime_factor(n)
  prime_factors(n).max
end

def prime_factors(n)
  if n == 1
    []
  else
    f = (2..n).find { |c| n % c == 0 }
    [f] + prime_factors(n / f)
  end
end

除了更短,更快,在我的机器上不到1毫秒(再次,不计算Ruby进程启动时间)因素600851475143。

答案 2 :(得分:2)

关于您的is_prime?方法:

  1. Ruby惯例建议您将其命名为prime?,而不是is_prime?
  2. 它无法正确处理n为一个或更少
  3. 的情况
  4. #select会在找到匹配后继续迭代;为了获得最佳性能,它应该尽快摆脱循环
  5. 快速优化是对任何可被两个
  6. 整除的数字返回false
  7. 你的循环可以从三开始,只能迭代奇数
  8. 您的循环只需迭代到n的平方根,而不是n
  9. 最近有类似的讨论enter image description here,我发布了一个更快prime?方法here