至少,你的代码应该是
primes = [2,3,5,7,11,13]
genPrimes primes max = go primes (length primes) 1 (last primes + 2)
where
go prs len d t
| len >= max = prs
| (prs !! d) > (floor . sqrt . fromIntegral) t
= go (prs++[t]) (len+1) 1 (t + 2)
| t `rem` (prs !! d) == 0 = go prs len 1 (t + 2)
| t `rem` (prs !! d) /= 0 = go prs len (d + 1) t
test n = print $ genPrimes primes n
main = test 20
然后你重新组织这样(抽象出每个候选号码进行的测试,作为noDivs
功能):
genPrimes primes max = go primes (length primes) (last primes + 2)
where
go prs len t
| len >= max = prs
| noDivs (floor . sqrt . fromIntegral $ t) t prs
= go (prs++[t]) (len+1) (t + 2)
| otherwise = go prs len (t + 2)
noDivs lim t (p:rs)
| p > lim = True
| t `rem` p == 0 = False
| otherwise = noDivs lim t rs
那么你重写noDivs
为
noDivs lim t = foldr (\p r -> p > lim || rem t p /= 0 && r) False
然后你注意到go
只是通过这样的筛选数字,通过noDivs
测试:
genPrimes primes max = take max (primes ++ filter theTest [t, t+2..])
where
t = last primes + 2
theTest t = noDivs (floor . sqrt . fromIntegral $ t) t
但这并没有工作,因为theTest
需要通过primes
(全新的素数,因为他们正在找到)到noDivs
,但我们正在建设这个whole_primes
列表(因为take max (primes ++ ...)
),所以有一个恶性循环?不,因为我们只测试一个数字的平方根:
genPrimes primes max = take max wholePrimes
where
wholePrimes = primes ++ filter theTest [t, t+2..]
t = last primes + 2
theTest t = noDivs (floor . sqrt . fromIntegral $ t) t wholePrimes
这是现在的工作。但最后,没有什么特殊的genPrimes
现在,它只是take
美化了电话,和初始primes
名单实际上可以缩小,所以我们得到(改变参数安排noDivs
一点,使其界面更加通用):
primes = 2 : 3 : filter (noDivs $ tail primes) [5, 7..]
noDivs factors t = -- return True if the supplied list of factors is too short
let lim = (floor . sqrt . fromIntegral $ t)
in foldr (\p r-> p > lim || rem t p /= 0 && r) True factors
-- all ((/=0).rem t) $ takeWhile (<= lim) factors
-- all ((/=0).rem t) $ takeWhile ((<= t).(^2)) factors
-- and [rem t f /= 0 | f <- takeWhile ((<= t).(^2)) factors]
全局primes
列表现在被无限期地定义(即“无限”)。 Next step是要认识到,在质数的连续平方之间,要测试的因子列表的长度将是相同的,对于每个新的分段递增1。我们可以直接生成它们的倍数(因此每一个都是从它的主要因素中生成的),而不是测试每个数字是否是它是是其平方根以下任何一个素因子的倍数。
除了提供错误的代码之外,发布确切的错误消息通常是一个好主意。这使我们更容易帮助你。 – dave4420 2011-12-25 09:02:28