Warning: file_get_contents(/data/phpspider/zhask/data//catemap/6/haskell/10.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Haskell 哈斯克尔是如何立即计算出这个庞大的数字的?_Haskell - Fatal编程技术网

Haskell 哈斯克尔是如何立即计算出这个庞大的数字的?

Haskell 哈斯克尔是如何立即计算出这个庞大的数字的?,haskell,Haskell,我开始学习Haskell,当我学习一门新语言时,我喜欢做的一件事就是做一些问题,作为我主要参考资料的补充 对于第二个问题,我提出了以下解决方案,即求小于400万的偶数斐波那契数之和: fibs = 0 : 1 : zipWith (+) fibs (tail fibs) f :: Integer -> Integer f n = let evenFib = filter (\n -> n `mod` 2 == 0) fibs in sum (takeWhile (<n)

我开始学习Haskell,当我学习一门新语言时,我喜欢做的一件事就是做一些问题,作为我主要参考资料的补充

对于第二个问题,我提出了以下解决方案,即求小于400万的偶数斐波那契数之和:

fibs = 0 : 1 : zipWith (+) fibs (tail fibs)
f :: Integer -> Integer
f n =
  let evenFib = filter (\n -> n `mod` 2 == 0) fibs
  in sum (takeWhile (<n) evenFib)
这些值中的每一个都会立即返回。我无法保证最后两个答案的准确性,因为我在其他语言中的实现对这么大的数字不起作用


所以,我的问题是,哈斯克尔在这里做什么?它是如何即时返回这些值的(不管它们是否正确)?此外,这些答案真的正确吗,还是哈斯克尔只是在瞎编

这可能与Haskell无关,但与其他解决方案使用的算法无关

由于斐波那契数增长非常快(平均每一步增长1.6倍),因此小于40000000000000000000000000的斐波那契数并不多,可能小于100

一台计算机添加少于100个这样大小的数字(不是特别大)需要几微秒的时间

我不确定您的其他实现是什么样子,但一个常见的错误是这样编写斐波那契函数:

fib 0 = 0
fib 1 = 1
fib n = fib (n-1) + fib (n-2)
这很糟糕,因为
fib n
调用
fib(n-1)
,然后调用
fib(n-2)
,并返回
fib n
的答案。但是你必须再次计算fib(n-2),因为你没有保存答案

在Haskell(或任何其他语言)中更好地实现
fib
,如下所示:

fib 0 = 0
fib n = fib' 0 1 n

fib' _ curr 1 = curr
fib' last curr n = fib' curr (last+curr) (n-1)

请注意,每个
fib'
调用只生成一个递归all,而不是两个。我上面写的大概是
0:1:zipWith(+)fibs(tail fibs)
正在做的事情,但是上面的代码有点混乱,但可能更容易翻译成其他语言。

答案应该是正确的

您可以
:设置+s
以使ghci打印内存/时间信息

再次运行测试时,您可以看到它实际上正在使用越来越多的内存:

Prelude> :set +s
Prelude> :{
Prelude| fibs = 0 : 1 : zipWith (+) fibs (tail fibs)
Prelude| f :: Integer -> Integer
Prelude| f n =
Prelude|   let evenFib = filter (\n -> n `mod` 2 == 0) fibs
Prelude|   in sum (takeWhile (<n) evenFib)
Prelude| :}
(0.14 secs, 0 bytes)
Prelude> f 40000000
19544084
(0.02 secs, 83,440 bytes)
Prelude> f 40000000
19544084
(0.01 secs, 83,200 bytes)
Prelude> f 400000000000
478361013020
(0.01 secs, 94,800 bytes)
Prelude> f 40000000000000000000000000000000
13049874051046942401006156573274
(0.01 secs, 149,400 bytes)
Prelude> f 2370498572349582734598273495872349587234958723948752394857
2805750129675962215536656398462489370528480907433875715844
(0.01 secs, 225,488 bytes)

这个数字增长很快,所以真的没有太多的工作要做。

正如其他答案中提到的,它非常快。虽然它已经很快了,但我仍然有一个更有效的方法依靠懒惰

对于像生成斐波那契级数这样的工作,我相信
unfover
是理想的工具,然后我们需要做的就是使用
foldr1
来获得结果。在本例中,
foldr1
体现了
takeWhile
filter
的功能,并在一次过程中完成相同的工作。所以懒散的展开和折叠,O(n)而已

fibs::[Integer]
fibs=展开器(\(f,s)->仅(f,(s,f+s))(0,1)
sumEvenFibsUpto::Integer->Integer
sumEvenFibsUpto n=foldr1(\x y->如果xsumEvenFibsUpto 237049857234958273459827349587234958234958729587239487752394857
2805750129675962215536656398462489370528480907433875715844
(0.01秒,324392字节)

(1)整数只是一种任意精度的整数类型。对于您正在使用的其他语言,可能有一些等效的东西可用。(2) 三个提示:(a)它真的是瞬间的吗?(b) 斐波那契数增长有多快?(c) 在每种情况下,实际求和的值是多少?算法的复杂度是O(log(n))。这就是为什么它这么快,有道理。谢谢你的回答,也谢谢你给我看这个很酷的ghci命令!这太棒了。感谢您向我展示您的实现。我喜欢Haskell。我的尾部递归Scala解决方案可能是它得到的最好的解决方案,但它的问题是使用了错误的类型。我的Python解决方案不是很有效。谢谢你的回答。
Prelude> :set +s
Prelude> :{
Prelude| fibs = 0 : 1 : zipWith (+) fibs (tail fibs)
Prelude| f :: Integer -> Integer
Prelude| f n =
Prelude|   let evenFib = filter (\n -> n `mod` 2 == 0) fibs
Prelude|   in sum (takeWhile (<n) evenFib)
Prelude| :}
(0.14 secs, 0 bytes)
Prelude> f 40000000
19544084
(0.02 secs, 83,440 bytes)
Prelude> f 40000000
19544084
(0.01 secs, 83,200 bytes)
Prelude> f 400000000000
478361013020
(0.01 secs, 94,800 bytes)
Prelude> f 40000000000000000000000000000000
13049874051046942401006156573274
(0.01 secs, 149,400 bytes)
Prelude> f 2370498572349582734598273495872349587234958723948752394857
2805750129675962215536656398462489370528480907433875715844
(0.01 secs, 225,488 bytes)
Prelude> sequence_ $ map (putStrLn . show) $ take 20 $ filter ((== 0) . (flip mod 2)) $ fibs
0
2
8
34
144
610
2584
10946
46368
196418
832040
3524578
14930352
63245986
267914296
1134903170
4807526976
20365011074
86267571272
365435296162
(0.02 secs, 203,472 bytes)
fibs :: [Integer]
fibs = unfoldr (\(f,s) -> Just (f,(s,f+s))) (0,1)

sumEvenFibsUpto :: Integer -> Integer
sumEvenFibsUpto n =  foldr1 (\ x y -> if x < n then if x `rem` 2 == 0 then x + y
                                                                      else y
                                               else 0) fibs

*Main> sumEvenFibsUpto 2370498572349582734598273495872349587234958723948752394857
2805750129675962215536656398462489370528480907433875715844
(0.01 secs, 324,392 bytes)