TL; DR:你做的两件事不是最优的,是:不停留在平方根,不划分每个最小的因子,因为它们被发现了。
这是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
因此它是素数。如果不是,我们会首先找到它的最小除数——矛盾。
所以,我们可以把它划分出来,继续寻找新约数的素因数:
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:空列表”错误换成了非终止但富有成效的行为。
最后,我们将f22
它们分成两部分并将它们分别融合到其他函数中,以简化代码。此外,我们不会再重新开始,就像一直从2f12
中搜索最小除数一样:
-- 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)
这通过高阶函数表达了受保护的(共)递归,并且在功能上等同于上面提到的代码。以下现在运行顺利,我们甚至可以在那里找到一对孪生素数作为奖励: iterate
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中断。