まず、変数と関数をいくつか用意する。
- a:総額
- k:コインの種類
- d:コインの種類に応じて金額を返す関数
- f:両替問題を解く関数
SICPでの考え方は、次のとおり。
f ( a , d ( k )) = f ( a - d ( k ), d ( k )) + f ( a , d ( k - 1))
これを式変形していくと、次を得る。
f ( a , d ( k )) = ∑_i=0→k-1 { f ( a - d ( k - i ), d ( k - i ))} + f ( a , d ( 0 ))
最後の項は定義により 0 だから、結局 f についての項和が得られる。この和をダイレクトにSchemeで書くと、次のようになる。
(define (d k)
(cond ((= k 1) 1)
((= k 2) 5)
((= k 3) 10)
((= k 4) 25)
((= k 5) 50)))
(define (sum-cc cc a k)
(cond ((< a 0) 0)
((or (= a 1) (= k 1)) 1)
(else (+ (cc (- a (d k)) k)
(sum-cc cc a (- k 1))))))
(define (cc a k)
(sum-cc cc a k))
(define (count-change a)
(cc a 5))
これは結局SICPの例と等価だよな。sum-ccだけ見れば普通の総和計算のコードなので、頭を捻って継続引き渡しスタイルで書くとかすれば、「見た目」は末尾再帰にできそうな木がする。それに、もうちょっとSICPを真面目に読み進めれば、∑を末尾再帰で書くヒントが書いてありそうな木がする。でも、評価の順番を書き下していけば結局SICPの例と同じ木になるわけで、そんなんじゃちっともうれしくないね。このあたりでいつもつまずく。SICPの第1章で主張しているのは、計算プロセスをrecursiveからiterativeにすれば計算機コストの面ではうれしい、って話だと思うんだけど、それってアルゴリズム(っていうか問題の解法)の話じゃないんだよねきっと。かといってプロシージャの表現の話でもないし。
ところでiterativeの訳語って逐次的?
1 件のコメント:
「効率化」になるのかなあ?
(cf.) Scheme:末尾再帰で木をトラバース
コメントを投稿