斐波那契&x27;s闭式表达式、ST monad和Haskell

斐波那契&x27;s闭式表达式、ST monad和Haskell,haskell,fibonacci,Haskell,Fibonacci,最近关于斐波那契闭式表达式(和)的两个问题,以及这个问题促使我尝试比较两种计算斐波那契数的方法 第一个实现使用封闭形式表达式和有理数,如中所示(其中Fib是抽象形式a+b的数字的数据类型)*√5) : 第二个实现来自Haskell Wiki关于ST monad的页面,为了避免堆栈溢出,增加了一些必要的严格性: fibST :: Integer -> Integer fibST n | n < 2 = n fibST n = runST $ do x <- newSTRe

最近关于斐波那契闭式表达式(和)的两个问题,以及这个问题促使我尝试比较两种计算斐波那契数的方法

第一个实现使用封闭形式表达式和有理数,如中所示(其中Fib是抽象形式a+b的数字的数据类型)*√5) :

第二个实现来自Haskell Wiki关于ST monad的页面,为了避免堆栈溢出,增加了一些必要的严格性:

fibST :: Integer -> Integer
fibST n | n < 2 = n
fibST n = runST $ do
    x <- newSTRef 0
    y <- newSTRef 1
    fibST' n x y
  where
    fibST' 0 x _ = readSTRef x
    fibST' !n x y = do
      x' <- readSTRef x
      y' <- readSTRef y
      y' `seq` writeSTRef x y'
      x' `seq` writeSTRef y (x'+y')
      fibST' (n-1) x y

所以我的问题是:有人能帮我理解为什么第一个实现要快得多吗?完全是算法复杂性、开销还是其他原因?(我检查了两个函数是否产生相同的结果)。谢谢

首先,这两种实现使用两种非常不同的算法,具有不同的渐近复杂度(取决于整数运算的复杂度)。 其次,st实现使用引用。ghc中的引用(相对)较慢。(因为更新引用需要GC写屏障,这是由于分代垃圾收集器造成的。)

所以,您正在比较两个在算法和实现技术上都不同的函数。
您应该重写第二个,不使用引用,这样您就可以只比较算法。或者重写第一个以使用引用。但是,如果引用是错误的,为什么还要使用引用呢?:)

首先,这两种实现使用两种非常不同的算法,具有不同的渐近复杂度(取决于整数运算的复杂度)。 其次,st实现使用引用。ghc中的引用(相对)较慢。(因为更新引用需要GC写屏障,这是由于分代垃圾收集器造成的。)

所以,您正在比较两个在算法和实现技术上都不同的函数。
您应该重写第二个,不使用引用,这样您就可以只比较算法。或者重写第一个以使用引用。但是,如果引用是错误的,为什么还要使用引用呢?:)

您正在比较非常不同的版本。为了公平起见,这里有一个与您给出的
ST
解决方案等效的实现,但使用纯Haskell:

fibIt :: Integer -> Integer
fibIt n | n < 2 = n
fibIt n = go 1 1 (n-2)
  where go !_x !y  0 = y
        go !x  !y  i = go y (x+y) (i-1)
fibIt::Integer->Integer
fibIt n | n<2=n
fibIt n=go 1(n-2)
去哪_x!y0=y
走!x!yi=go-y(x+y)(i-1)

这个版本的性能似乎与
ST
版本(这里都是10)一样好或坏。运行时很可能由所有的
整数
加法控制,因此开销太低,无法测量。

您正在比较非常不同的版本。为了公平起见,这里有一个与您给出的
ST
解决方案等效的实现,但使用纯Haskell:

fibIt :: Integer -> Integer
fibIt n | n < 2 = n
fibIt n = go 1 1 (n-2)
  where go !_x !y  0 = y
        go !x  !y  i = go y (x+y) (i-1)
fibIt::Integer->Integer
fibIt n | n<2=n
fibIt n=go 1(n-2)
去哪_x!y0=y
走!x!yi=go-y(x+y)(i-1)

这个版本的性能似乎与
ST
版本(这里都是10)一样好或坏。运行时很可能由所有的
整数
加法控制,因此开销太低,无法测量。

您可以比较算法的复杂性

第一个是O(1)


第二个是O(n)

您可以比较算法的复杂性

第一个是O(1)


第二个是O(n)

你可以通过不使用Rational来加速第一个。你可以通过不使用Rational来加速第一个。第一个的复杂性不是
O(1)
。求幂需要
O(logn)
乘法,因为最终结果有θ(n)位,所以总体复杂度不能小于
O(n)
。我看我错了。你如何计算第二部分,找到下限?第一部分的复杂性不是
O(1)
。求幂需要
O(logn)
乘法,因为最终结果有θ(n)位,所以总体复杂度不能小于
O(n)
。我看我错了。你如何计算第二部分,找到下限?
# time ./fib rt 1000000 >/dev/null
./fib rt 1000000 > /dev/null  0.23s user 0.00s system 99% cpu 0.235 total

# time ./fib st 1000000 >/dev/null
./fib st 1000000 > /dev/null  11.35s user 0.06s system 99% cpu 11.422 total
fibIt :: Integer -> Integer
fibIt n | n < 2 = n
fibIt n = go 1 1 (n-2)
  where go !_x !y  0 = y
        go !x  !y  i = go y (x+y) (i-1)