第2種スターリング数とフィボナッチ数
第2種スターリング数を求める関数を書いていた。
;n<20程度にする (defun stirling-2nd-kind (n k) (cond ((= n k) 1) ((zerop k) 0) ((< n k) 0) (t (+ (stirling (1- n) (1- k)) (* k (stirling (1- n) k))))))
これだと,多重再帰となって非常に計算が遅いので,せいぜいn=20を超えるくらいまでしか計算できない。そこで,再帰のない形にできないかと思い,フィボナッチ数が似ているなと思って検索したら,Common Lispの色々なループ - ありの日記を見つけた。コメント欄が興味深かったので,丸写しみたいなものだがフィボナッチ数について書いてみる。
;n<20程度にする (defun fib (n) (cond ((< n 1) 0) ((= n 1) 1) (t (+ (fib (1- n)) (fib (- n 2)))))) ;nにあまり大きい数値を入れない (defun fib1 (n) (labels ((iter (m a b) (if (= m n) a (iter (1+ m) b (+ a b))))) (if (< n 1) 0 (iter 1 1 1)))) (defun fib2 (n) (do ((i 0 (1+ i)) (a 0 b) (b 1 (+ a b))) ((<= n i) a)))
fibはほぼ定義通りで,nが負数を入れると止まるのを防ぐように直してあるだけである。これは多重再帰なので非常に遅い。そこで,末尾再帰になるようにfib1のように書きなおすわけだが,何か回りくどいやり方をしているように見える。一番効率的なのがループ関数を使う方法で,これがfib2になる。特にdoは美しく書くことができ,数列計算程度では内部式を書く必要すらない! というところに感銘を受けた次第である。
下の2つはフィボナッチ数の小さい方から順に数えているから,計算結果を順次用いることができるわけだ。第2種スターリング数も,2次元の表を作る必要があるが,同様の計算をすることができるだろう。今は時間が無いので,後で書くか調べることにする。