好吧,你有两个错误:你的
isprimerec _ 1 = False
isprimerec n t = if (n `rem` t) == 0 then False else isprimerec n (n-1)
本来应该
isprimerec _ 1 = True
isprimerec n t = if (n `rem` t) == 0 then False else isprimerec n (t-1)
或者,通过列表理解,
isprime n = n>1 && and [ rem n t /= 0 | t <- [n-1,n-2..2] ]
(内部化那个额外的参数t
,无论如何它是一个技术性问题!--啊哈,但是那是什么and
,你问?就像这个递归函数一样foldr (&&) True :: [Bool] -> Bool
。)
但是现在一个主要的算法缺陷在视觉上变得很明显:我们以错误的顺序进行测试。如果我们按升序测试会更快:
isprime n = n>1 && and [ rem n t /= 0 | t <- [2..n-1] ]
sqrt
如果我们停在,甚至更快
isprime n = n>1 && and [ rem n t /= 0 | t <- [2..q] ]
where q = floor (sqrt (fromIntegral n))
或仅在2之后通过2进行测试(如果我们已经通过2进行了测试,为什么要通过6进行测试?):
isprime n = n>1 && and [ rem n t /= 0 | t <- 2:[3,5..q] ]
where q = floor (sqrt (fromIntegral n))
或仅按素数(如果我们已经通过3测试,为什么要按9测试?):
isprime n = n>1 && and [ rem n t /= 0 | t <- takeWhile ((<= n).(^2)) primes ]
primes = 2 : filter isprime [3..]
为什么在过滤素数时测试偶数- 首先不生成它们不是更好吗?
primes = 2 : filter isprime [3,5..]
但isprime
总是测试除以2——但我们只给它输入奇数;所以,
primes = 2 : 3 : filter (noDivs (drop 1 primes)) [5,7..]
noDivs factors n = and [ rem n t /= 0 | t <- takeWhile ((<= n).(^2)) factors ]
为什么要生成3的倍数(即[9,15 ..] == map (3*) [3,5..]
),只是为了稍后测试和删除它们?--
{- [5,7..]
== [j+i | i<-[0,2..], j<-[5]] -- loop unrolling, 3x:
== [j+i | i<-[0,6..], j<-[5,7,9]]
== 5:[j+i | i<-[0,6..], j<-[7,9,11]]
== 5:[7,9,11,13,15,17,19,21,23,25,27,29,31,33,35,37,39,41,43, ...
\\ [ 9, 15, 21, 27, 33, 39, ...
== [j+i | i<-[0,6..], j<-[ 9 ]]
-}
primes = 2:3:5: filter (noDivs (drop 2 primes))
[j+i | i<-[0,6..], j<-[7, 11]]
我们也可以提前跳过5的倍数(作为欧拉筛的另一个步骤, ):euler (x:xs) = x : euler (xs `minus` map (x*) (x:xs))
-- [j+i | i<-[0, 6..], j<-[7, 11]] -- loop unrolling, 5x:
-- == 7:[j+i | i<-[0,30..], j<-[11,13,17,19,23,25,29,31,35,37]]
-- \\ [j+i | i<-[0,30..], j<-[ 25, 35 ]]
primes = 2:3:5:7: filter (noDivs (drop 3 primes))
[j+i | i<-[0,30..], j<-[11,13,17,19,23, 29,31, 37]]
......但这已经够远了,就目前而言。