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