有一种更有效的分解方法,它也只找到素因数,消除了测试素数的需要。跳到最后查看或通读以了解我是如何到达那里的。
我重构了您的解决方案,以便更容易思考,提取 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
除了更短之外,它还更快,在我的机器上将 600851475143 分解为不到 1 毫秒(同样,不计算 Ruby 进程启动时间)。