一个简单的程序,用于在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.当尝试这个时,问题甚至不能运行,我最终取消了它大约一分钟。
如何更改此功能以提高性能?
答案 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?
方法:
prime?
,而不是is_prime?
n
为一个或更少#select
会在找到匹配后继续迭代;为了获得最佳性能,它应该尽快摆脱循环n
的平方根,而不是n
最近有类似的讨论,我发布了一个更快prime?
方法here。