TL;DR:你做的两件事不是最优的,是:不停留在平方根,不划分每个最小的因素,因为它们找到了。
这是the answer by HaskellElephant 中显示的(第二个)分解代码的一个小推导。我们从您的代码开始:
f1 n = [ x | x <- [2..n], rem n x == 0]
n3 = 600851475143
Prelude> f1 n3
[71,839,1471,6857,59569,104441,486847Interrupted.
所以它不会在任何合理的时间内完成,并且它产生的一些数字不是 素数...但是不是在列表理解中添加素数检查,让我们注意71 是素数。 f1 n 产生的第一个数字是n 的最小除数,因此它是素数 .如果不是,我们会首先找到 它的最小除数 - 矛盾。
所以,我们可以把它分出来,continue searching新约数的质因数:
f2 n = tail $ iterate (\(_,m)-> (\f->(f, quot m f)) . head $ f1 m) (1,n)
Prelude> f2 n3
[(71,8462696833),(839,10086647),(1471,6857),(6857,1),(*** Exception: Prelude.hea
d: empty list
(错误,因为f1 1 == [])。我们完成了! (6857 是答案,这里...)。让我们总结一下:
takeUntil p xs = foldr (\x r -> if p x then [x] else x:r) [] xs
pfactors1 n = map fst . takeUntil ((==1).snd) . f2 $ n -- prime factors of n
试用我们新开发的解决方案,
Prelude> map pfactors1 [n3..]
[[71,839,1471,6857],[2,2,2,3,3,1259Interrupted.
突然间,我们在没有小除数的数字上遇到了新的低效率墙。但是如果n = a*b 和1 < a <= b,那么a*a <= a*b == n 等就足以测试直到一个数字的平方根,找到它的最小除数。
f12 n = [ x | x <- takeWhile ((<= n).(^2)) [2..n], rem n x == 0] ++ [n]
f22 n = tail $ iterate (\(_,m)-> (\f->(f, quot m f)) . head $ f12 m) (1,n)
pfactors2 n = map fst . takeUntil ((==1).snd) . f22 $ n
半小时内无法完成的事情现在在一秒钟内完成(在典型的高性能机器上):
Prelude> f12 n3
[71,839,1471,6857,59569,104441,486847,600851475143]
sqrt n3 上面的所有除数根本不需要。我们无条件地将n 本身添加为f12 中的最后一个除数,以便它能够处理素数:
Prelude> f12 (n3+6)
[600851475149]
从n3 / sqrt n3 = sqrt n3 ~= 775146 开始,您最初对f1 n3 的尝试应该需要大约一周 才能完成。这就是优化在平方根处停止的重要性。
Prelude> f22 n3
[(71,8462696833),(839,10086647),(1471,6857),(6857,1),(1,1),(1,1),(1,1),(1,1),(1,
1),(1,1),(1,1),(1,1),(1,1),(1,1),(1,1),(1,1),(1,1),(1,1)Interrupted
我们显然已经将 “Prelude.head: empty list” 错误换成了非终止 - 但生产 - 行为。
最后,我们将f22 分成两部分,并将它们分别融合到其他函数中,以简化代码。此外,我们不会重新开始,就像 f12 所做的那样,一直在寻找 2 中的最小除数:
-- smallest factor of n, starting from d. directly jump from sqrt n to n.
smf (d,n) = head $ [ (x, quot n x) | x <- takeWhile ((<=n).(^2)) [d..]
, rem n x == 0] ++ [(n,1)]
pfactors n = map fst . takeUntil ((==1).snd) . tail . iterate smf $ (2,n)
这通过higher-order functioniterate 表达受保护的(共同)递归,并且在功能上等同于上述代码。以下现在运行顺利,我们甚至可以在那里找到一对twin primes 作为奖励:
Prelude Saga> 地图 pfactors [n3..]
[[71,839,1471,6857],[2,2,2,3,3,1259,6628403],[5,120170295029],[2,13,37,227,27514
79],[3,7,7,11,163,2279657],[2,2,41,3663728507],
[600851475149],[2,3,5, 5,19,31,680
0809],
[600851475151],[2,2,2,2,37553217197],[3,3,3,211,105468049],[2,7,11161, 3845
351],[5,67,881,2035853],[2,2,3中断。