プログラミングGauche p.226 ストリームによるフィボナッチ数列の速さ
ストリームは遅延評価されるリスト。遅延評価も興味あるけど、この本「プログラミングGauche」では、索引を見る限り、ここでしか遅延評価の話は出てこないようだ。
さて、ストリームによるフィボナッチ数列が出てきた。フィボナッチ数列のN番目の数を得る計算について、他の手法との計算速度の比較をしてみる。まずはそれぞれの手法の定義から。
この本に載っていたストリームによるフィボナッチ数列の定義。(define fib (stream-cons 0 (stream-cons 1 (stream-map + fib (stream-cdr fib)))))
フィボナッチ数列のN番目の数を得る素朴な手続きの定義。(define (fib-n n) (cond [(<= n 1) n] [else (+ (fib-n (- n 1)) (fib-n (- n 2)))]))
フィボナッチ数列のN番目の数を得る素朴な手続きの定義。ただし末尾再帰。(define (fib-t n) (letrec ((iter (lambda (i x y) (cond [(= i n) x] [else (iter (+ i 1) y (+ x y))])))) (iter 0 0 1)))
CPUはCore2Duo E7400。
gosh> ;(time (stream-ref fib 90000)) ; real 7.395 ; user 0.445 ; sys 2.320 #f gosh> ;(time (fib-n 35)) ; real 2.126 ; user 2.109 ; sys 0.000 #f gosh> ;(time (fib-t 90000)) ; real 2.735 ; user 0.883 ; sys 0.250 #f
圧倒的だな、末尾再帰は。計算速度だけ見れば末尾再帰は最良なのかも。
ここには載せてないけど、他の序数でも試してみた。ストリーム版も序数が小さいうちは、末尾再帰版とほぼ同じ速さだった。けど、序数を大きくすると急に遅くなった。
実行時間中の、userとsysの分配にも結構な違いが現れてる。ストリーム版はuserが小さくて、sysが大きい。遅延評価はシステムコールに使う時間が長いのかな。