合并功能要慢得多

时间:2017-10-30 10:12:44

标签: performance haskell primes primality-test

我写了isPrime函数。它检查给定的数字是否为素数。 最后一个" prime"列表单独给出。

prime :: [Integer]
prime = 2 : filter isPrime [3..]
isPrime :: Integer -> Bool
isPrime n | n < 2 = False
isPrime n = all (\p -> n `mod` p /= 0) . takeWhile ((<=n) . (^2)) $ prime

我认为如果可能的话,将两个函数合并为一个总是更好。所以我将isPrime和prime合并到一个函数isPrime2中。但isPrime2的表现非常糟糕。

isPrime2 :: Integer -> Bool
isPrime2 n | n < 2 = False
isPrime2 n = all (\p -> n `mod` p /= 0) . takeWhile ((<=n) . (^2)) $ 2 : filter isPrime2 [3..]

isPrime 40000000000000000001

=&GT; 0.5秒

isPrime2 40000000000000000001

=&GT; 19.8秒

我的机器是Ubuntu 17.10 x86-64。我正在使用ghc 8.2.1。有谁知道为什么?

1 个答案:

答案 0 :(得分:6)

第一个代码段只会在内存中保留一个prime列表。

第二个代码段将计算自己的prime,直到n^2 每次调用 isPrime2。先前发现的素数被丢弃并在每次调用时从头开始重新计算。 由于isPrime2是递归的,因此会导致爆炸。

中间方法可以是这个:

isPrime2 :: Integer -> Bool
isPrime2 m = isPrime m
   where
   prime :: [Integer]
   prime = 2 : filter isPrime [3..]
   isPrime :: Integer -> Bool
   isPrime n | n < 2 = False
   isPrime n = all (\p -> n `mod` p /= 0) . takeWhile ((<=n) . (^2)) $ prime

这会在prime的每次通话中重新计算isPrime2,但不会导致爆炸,因为内部isPrime的每次调用都会共享同一个{{1}列表}}第