Fibonacci位表示Haskell

时间:2018-01-24 07:01:38

标签: haskell functional-programming fibonacci

我已经拥有以下功能

toBin, auxBin :: Integer -> [Integer]
toBin 0 = [0]
toBin n = reverse (auxBin n)

auxBin 0 = []
auxBin n = n `mod` 2 : auxBin (n `div` 2)

fib :: Int -> Integer
fib n = fibs !! n
  where
    fibs = 0 : 1 : zipWith (+) fibs (tail fibs)

fibonacci = [fib n | n <- [0..]]

但是当我使用toBin函数映射Fibonacci列表时,我得到了一个不正确的列表:

因为我得到了这个:

[[0],[1],[1],[1,0],[1,1],[1,0,1],[1,0,0,0],[1,1,0,1],[1,0,1,0,1],[1,0,0,0,1,0]]

但是,我想要这个:

[0,1,10,101,1010,10101,101010,1010101,10101010,101010101]

你能帮帮我吗?

2 个答案:

答案 0 :(得分:4)

有很多方法可以将数字加在一起以获得数字。这是一种方式。可能不是最有效的方式,但另一方面是由较小的功能构建的。

[1,0,1][1,0,0,0][1,1,0,1]这样的值本身就是列表,所以我们首先要做的就是索引它们。唯一的麻烦是我们想按降序索引它们。你可以通过反转它们,索引它们然后再反转它们来做到这一点,例如:

Prelude> reverse $ zip [0..] $ reverse [1,1,0,1]
[(3,1),(2,1),(1,0),(0,1)]

每个元组中的第一个元素是数量级,所以你只需要使它成为10的幂:

Prelude> :m +Data.Bifunctor
Prelude Data.Bifunctor> reverse $ fmap (first (10 ^)) $ zip [0..] $ reverse [1,1,0,1]
[(1000,1),(100,1),(10,0),(1,1)]

现在你可以简单地将元组的元素相乘:

Prelude Data.Bifunctor> reverse $ fmap (uncurry (*) . first (10 ^)) $ zip [0..] $ reverse [1,1,0,1]
[1000,100,0,1]

最后,您可以将所有这些数字加在一起。实际上,您不需要反转反转列表:

Prelude Data.Bifunctor> sum $ fmap (uncurry (*) . first (10 ^)) $ zip [0..] $ reverse [1,1,0,1]
1101

您可以将这样的组合放在一个函数中,并将值映射到它上面。

更有效的解决方案可能是做一个左侧折叠foldl),例如:

Prelude> foldl (\acc x -> (10 * acc) + x) 0 [1,1,0,1]
1101
Prelude> foldl (\acc x -> (10 * acc) + x) 0 [1,1,1]
111

答案 1 :(得分:1)

一个想法可能是用十进制表示法表示二进制值。因此,我们将2“转换为10

我们可以通过编写递归函数来实现:

bintodec :: Integral i => i -> i
bintodec 0 = 0
bintodec i = (mod i 2) + 10 * bintodec (div i 2)

这仅适用于正值,但这不是真正的问题,因为斐波纳契数是正数。

现在我们已经有了Fibonacci数字的定义,比如你的答案:

fibs :: Num n => [n]
fibs = 0 : 1 : zipWith (+) fibs (tail fibs)

所以我们唯一需要做的就是map fibs bintodec的所有元素:

binfibs :: Integral i => [i]
binfibs = map bintodec fibs

然后是前15个数字:

Prelude> take 15 binfibs
[0,1,1,10,11,101,1000,1101,10101,100010,110111,1011001,10010000,11101001,101111001]

好消息是,我们不使用任何二进制列表来处理它,而是继续在整数世界中工作,这通常更安全。

然而,基于第二个列表,这与Fibonacci数字无关。在那里,您从0开始,然后在数字的右端移动零或一个。

我们可以使用iterate

iterate (\x -> 10 * x + 1-(mod x 2)) 0

产生:

Prelude> take 15 $ iterate (\x -> 10 * x + 1-(mod x 2)) 0
[0,1,10,101,1010,10101,101010,1010101,10101010,101010101,1010101010,10101010101,101010101010,1010101010101,10101010101010]