【问题标题】:Why is the following Haskell code hanging?为什么下面的 Haskell 代码会挂起?
【发布时间】:2015-12-29 05:13:03
【问题描述】:

我是 Haskell 的新手。我试图解决丢番图方程 |x^y-y^x|是素数,使用 Haskell,对于给定的上限 x, y 。

所以,我写了这个 Haskell 代码:

-- list of primes
listprimesupto :: Integral a => a -> [a]
listprimesupto 1 = []
listprimesupto 2 = [2]
listprimesupto n = let halflstprimes = (listprimesupto (n `div` 2))
                   in halflstprimes++[i|i<-[((n `div` 2)+1)..n], (length [x|x<-halflstprimes, (i `mod` x) == 0])==0 ]

-- is prime?
is_prime :: Integral a => a -> Bool
is_prime 1 = False
is_prime n = let halflstprimes = (listprimesupto (n `div` 2))
             in (length [x|x<-halflstprimes, (n `mod` x) == 0])==0           

-- solve |x^y - y^x| == prime
xy_yx_p :: Integral t => t -> [(t, t)]
--xy_yx_p n = [(x,y)|x<-[2..n], y<-[2..n], x < y, (abs (x^y-y^x)) `elem` (listprimesupto (n^3))] -- version 1, works but upper limit too small
xy_yx_p n = [(x,y)|x<-[2..n], y<-[2..n], x < y, (let t=abs (x^y-y^x) in is_prime t)==True] -- version 2, hangs for n>3 ...

xy_yx_p n(版本 2,未注释)在 GHCi 中挂起 n > 3。 Ctrl-C 甚至不起作用。我必须从活动监视器中杀死 ghc(我在 Mac 上)。

知道我在xy_yx_p 中做错了什么吗?其他两个功能似乎工作正常。

提前致谢。

【问题讨论】:

  • 旁注:您可以轻松简化为[(x,y) | x &lt;- [2..n], y &lt;- [(x+1)..n], is_prime (abs (x^y - y^x))]。看看说== True 从来没有用处?
  • 请注意,length [x | x &lt;- xs, p x] == 0 可以写得更好,更懒惰,如all (not . p) xs
  • @Cactus:效率更高,因为它会在第一个元素上中断,而length xs 将不得不等待整个计算(除非这是自定义的length :: Foldable t =&gt; t -&gt; Peano 或类似的)。
  • 是的,我说的懒惰就是这个意思。但你强调它是短路是对的。
  • @dfeuer:是的,我试过了。我把这个表达式写得如此无聊的原因是因为我想确保我的语义是正确的。但是,在最初的形式中,它看起来像你建议的那样。谢谢。

标签: haskell


【解决方案1】:

那么,如果它为n = 4 挂起,那这个案子有什么特别之处呢?嗯,它是t。对于x = 2y = 4,你会得到

t = abs (2 ^ 4 - 4 ^ 2)
  = abs (16    - 16   )
  = abs 0
  = 0

因此,您在is_prime 中使用0,因此也在listprimesupto 中使用。这导致了一个永无止境的递归:

listprimesupto 0 = let halflstprimes = (listprimesupto (0 `div` 2))
                   in -- .....

所以请确保您处理非正输入:

listprimesupto n | n <= 0 = []

is_prime n | n <= 1 = False

【讨论】:

  • 如果您使用上述技术,5 不应挂起。但是另一个将花费 very 很长时间,因为您一遍又一遍地计算相同的列表。对于10,您需要的素数最高可达2486784401 = 10^9 - 9^10。这需要一段时间。
  • 我现在意识到 n=4 的挂起会触发任何高于 5 的挂起。因为案例 2^4-4^2 在 k^m-m^k 之前被测试,对于任何 k>2 和 m>4。所以,是的,你是对的 t=0 是问题所在。谢谢。
  • 关于计算时间太长:是的,我预料到了。此外,内存消耗预计会变得非常大,因为(至少我的理解是)整数类型可以变成内存可以容纳它们的整数。这是我为这个问题选择 Haskell 的原因之一。
  • Integral 仅表示类似整数。它是特定类型Integer,可以任意增长。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2012-12-09
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多