Haskell ++;,最后及;初始速度快于:、头和;尾巴?

Haskell ++;,最后及;初始速度快于:、头和;尾巴?,haskell,primes,Haskell,Primes,给定以下两种编写函数的方法,该函数可以查找特定数量的所有素数: primes1 = iterate (\ps -> ps ++ [([x | x <- [last ps + 1..], all (\p -> x `mod` p /= 0) ps] !! 0)]) [2] primesTo1 :: Integer -> [Integer] primesTo1 n = init $ head $ dropWhile (\p -

给定以下两种编写函数的方法,该函数可以查找特定数量的所有素数:

primes1 = iterate
    (\ps -> ps ++ [([x |
        x <- [last ps + 1..],
        all (\p -> x `mod` p /= 0) ps] !! 0)])
    [2]

primesTo1 :: Integer -> [Integer]
primesTo1 n = init $ head $ dropWhile (\p -> last p <= n) primes1

primes2 = iterate
    (\ps -> ([x |
            x <- [head ps + 1..],
            all (\p -> x `mod` p /= 0) ps] !! 0)
        : ps)
    [2]

primesTo2 :: Integer -> [Integer]
primesTo2 n = tail $ head $ dropWhile (\p -> head p <= n) primes2
使用ghc-O2编译的

$ time ./primes1
...
./primes1  0.06s user 0.00s system 68% cpu 0.089 total
$ time ./primes2
...
./primes2  0.28s user 0.00s system 98% cpu 0.283 total
$ time ./primes3
...
./primes  0.05s user 0.00s system 24% cpu 0.209 total

注意:我不是在为Haskell寻找一个最优的素数生成器,我只是被这两个函数的速度差异弄糊涂了。

正如“n.m.”所指出的,这是因为
primes2
首先尝试除以找到的最大素数,而
primes1
从最小的素数开始

因此,在
all
中使用当前素数之前,首先反转当前素数列表实际上比
primes1
primes2
都要快:

primes3 = iterate
    (\ps -> ([x |
            x <- [head ps + 1..],
            all (\p -> x `mod` p /= 0) $ reverse ps] !! 0)
        : ps)
    [2]

primesTo3 :: Integer -> [Integer]
primesTo3 n = tail $ head $ dropWhile (\p -> head p <= n) primes3
并使用ghc-O2编译:

$ time ./primes1
...
./primes1  0.06s user 0.00s system 68% cpu 0.089 total
$ time ./primes2
...
./primes2  0.28s user 0.00s system 98% cpu 0.283 total
$ time ./primes3
...
./primes  0.05s user 0.00s system 24% cpu 0.209 total
我知道你们说过“并没有为Haskell寻找一个最优的素数生成器”,但你们仍然对“两个函数的速度差”感兴趣。因此,这里有一些对已更正函数的语法操作,
primes3
,它通过
(:)
,以相反的顺序添加primes,并在每次测试时反转它们

primes3 :: [[Integer]]
primes3 = iterate
    (\ps -> ([x |
            x <- [head ps + 1..],
            all (\p -> x `mod` p /= 0) $
                takeWhile (\p-> p*p <= x) $ reverse ps] !! 0)
        : ps)                            -- ^^^^^^^^^^
    [2]
primes3::[[Integer]]
primes3=迭代
(\ps->)([x|
x`mod`p/=0)$
takeWhile(\p->p*p([x|
x`mod`p/=0)$
takeWhile(\p->p*p头[x | x`mod`p/=0)$
takeWhile(\p->p*p head[x | x`mod`p/=0)$tail$
takeWhile(\p->p*p((n+1,ps),
[x |设lst=取n$tail primes7,

x
primes1
自下而上扫描素数,
primes2
自上而下扫描。后者是一种非常糟糕的过滤复合数的缓慢方式。@n.m.谢谢,这很有道理。被2或3整除的数字比被997整除的要多得多。@DanielFischer我想n.m.已经说过了。我只是扩展了一下,解释了为什么会这样是一种缓慢的方法。尝试
all(\p->rem x p/=0)$takeWhile(\p->p*p With
ghci
(0.13秒,177274752字节)
,编译:
0.01s用户0.00s系统82%cpu 0.018总计
尝试两个大小点,例如n=10000和n=20000。然后在该范围内检查算法的
n^a
,使用
a=logBase(n2/n1)(t2/t1)
(对于10k对20k,
logBase 2(t2/t1)
):)也可以在原始算法中使用它
primes3b :: [[Integer]]
primes3b = iterate
    (\ps -> ([x |
            x <- [head ps + 1..],
            all (\p -> x `mod` p /= 0) $
                takeWhile (\p-> p*p <= x) $ map head $ primes3b ] !! 0)
        : ps)                            -- ^^^^^^^^^^^^^^^^^^^
    [2]
primes4 :: [Integer]
primes4 = map head $ iterate
    (\ps -> ([x |
            x <- [head ps + 1..],
            all (\p -> x `mod` p /= 0) $
                takeWhile (\p-> p*p <= x) primes4 ] !! 0)
        : ps)                          -- ^^^^^^^
    [2]
primes5 :: [Integer]
primes5 = iterate
    (\p -> head [x | x <- [p + 1..],
                 all (\p -> x `mod` p /= 0) $
                   takeWhile (\p-> p*p <= x) primes5 ] 
           ) -- nothing!
    2
primes6 :: [Integer]
primes6 = 2 : iterate
    (\p -> head [x | x <- [p + 2, p + 4..],
                 all (\p -> x `mod` p /= 0) $ tail $
                   takeWhile (\p-> p*p <= x) primes6 ] )
    3           -- ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
primes7 :: [Integer]
primes7 = concatMap snd $ iterate 
              (\((n,p:ps@(q:_)),_) -> ((n+1,ps),
                       [x | let lst = take n $ tail primes7,
                            x <- [p*p + 2, p*p + 4..q*q-2],
                            all ((/= 0).rem x) lst]))
              ((1,tail primes7),[2,3,5,7])