【问题标题】:consolidated function is much slower合并功能要慢得多
【发布时间】:2018-04-11 06:47:50
【问题描述】:

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

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

=> 0.5 秒

isPrime2 40000000000000000001

=> 19.8 秒

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

【问题讨论】:

  • 我的猜测是,因为prime 是一个常数,它会被记忆,而isPrime2 是一个函数,所以它不会。然而,这只是一个猜测......
  • 谢谢!你的解释给了我洞察力。
  • @eii0000 你是在测试它编译还是解释?如果您将isPrime2 n 简化为all (\p -&gt; n `mod` p /= 0) . takeWhile ((&lt;=n) . (^2)) $ 2 : [3,5..],它如何比较?
  • 所有 (\p -> n mod p /= 0) 。 takeWhile (( n mod p /= 0) 。 takeWhile ((
  • 我编译了它。谢谢!

标签: performance haskell primes primality-test


【解决方案1】:

第一个 sn-p 将在内存中只保留一个 primes 列表。

第二个 sn-p 将计算自己的 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

这将在每次调用isPrime2 时重新计算prime,但不会导致崩溃,因为内部isPrime 的每次调用都将共享相同的primes 列表。

【讨论】:

  • 太棒了……你的 isPrime2 也是 0.5 秒……谢谢!
  • 在优化过程中,GHC 通常不会将primeisPrime 浮动到顶层吗?
  • @BenjaminHodgson 不,GHC 在这里是保守的,因为它并不总是一种优化。我相信这种转换称为(或与之相关)“完全惰性”转换,其中\x -&gt; let y = ... in .... 变为let y= ... in \x -&gt; ...,前提是y 不依赖于x。语义得以保留,但很难预测哪一个具有最佳性能 (IIRC)。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2018-09-04
  • 2016-01-01
相关资源
最近更新 更多