数字计数器Fibonacci列出Haskell的麻烦

时间:2015-08-23 18:37:58

标签: haskell

因此,对于Project Euler中的问题25,我必须找到Fibonacci序列中第一个数字的位置,数字为千位。

-- Lazily generates an infinite Fibonacci series
fib :: [Int]
fib = 1 : 1 : zipWith (+) fib (tail fib)

-- Checks if given number has a thousand digits.
checkDigits :: Int -> Bool
checkDigits number = length (show number) == 1000

-- Checks if position in Fibonacci series has a thousand digits.
checkFibDigits :: Int -> Bool
checkFibDigits pos = checkDigits (fib !! (pos - 1))

p25 = head (filter (\x -> checkFibDigits x == True) ([1..]))

出于某种原因,这种方法似乎无限期地挂起。如果我用10替换1000,它会吐出45,这是第一个数字的位置,有10位数。

要么我的方法效率低下,要么哈斯克尔用大数字做一些奇怪的事情。 Python中的类似方法非常完美。

感谢您的帮助!

2 个答案:

答案 0 :(得分:3)

只需将Int改为Integer fibcheckDigits,您就会发现答案会立即出现:

fib :: [Integer]
fib = 1 : 1 : zipWith (+) fib (tail fib)

checkDigits :: Integer -> Bool
checkDigits number = length (show number) == 1000

这是因为Int的大小有限,而Integer具有受系统内存限制的任意精度。

答案 1 :(得分:3)

您的直接问题是在Int类型中使用Integer而不是fib,这会将值限制为永远不会超过2 31 ,但除此之外,是的,你这样做的方式是非常低效的。也就是说,当它真的应该是O(n)时,它是O(n 2 )。你生成Fibonacci序列的方式很好,但是当你试图找到千位数的第一个值时,你会去:

  • Fibonacci序列的第一个元素是否大于1000位?不,继续......
  • 是第二个元素[等等,我需要从这个链表中获取,所以我最好按照'下一个'指针多次]大于1000位?不,继续......
  • ...
  • 是第50个元素[更好地从链接列表的开头开始,按照下一个指针,按照下一个指针,按照下一个指针,......,并获取此元素的值]大于1000位?不,继续......
  • ...

基本上,您每次都会重新遍历链表。一种不同的方法可能是将索引和相应的Fibonacci结果压缩在一起:

ghci> take 10 $ zip [1..] fib
[(1,1),(2,1),(3,2),(4,3),(5,5),(6,8),(7,13),(8,21),(9,34),(10,55)]

然后你删除元素,直到Fibonacci值至少为1000位数,并取下第一个的索引:

ghci> fst $ head $ dropWhile ((< 1000) . length . show . snd) $ zip [1..] fib
4782