在《電腦程式的構造和解釋》(第2版)p26頁,作者用遞迴的方式為我們提供了一種解決思路:
將總數為a的現金換成n種硬幣的不同方式的數目等於
下面是我將書中scheme的方言換成common lisp實現的**:
(
defun
first-denomination
(kinds-of-coins) (
cond((
= kinds-of-coins 1)1
)((= kinds-of-coins 2)5
)((= kinds-of-coins 3)10
)((= kinds-of-coins 4)25
)((= kinds-of-coins 5)50
)))(
defuncc(
amount
kinds-of-coins) (
cond((
= amount 0)1
)((or
(< amount 0)(
= kinds-of-coins 0))
0)(t
(+(cc amount (
- kinds-of-coins 1))
(cc(- amount (
first-denomination kinds-of-coins)
) kinds-of-coins)))
))(defun
count-change
(amount) (
cc amount 5
))
下面在lispbox-0.7中執行的結果:
count-change產生出乙個樹形的遞迴計算過程,包含了不少冗餘計算。作者將設計乙個更好的演算法的問題作為了挑戰留給讀者。
我的演算法是先把所有的金額放在幣值最小的硬幣上,再依次遞減,遇到可以換成大面額的就遞迴進去,然後出來繼續遞減。
下面是演算法的**:
;;硬幣種類,已排序
(setf kinds-of-coins '((1
5)(5
2)(10
5)(50
2)(100
999)))
;;;第乙個元素是幣值,第二個是滿足進製的最小個數
(defun
count-change
(amount
kinds) (
dd(list
(list
(caar kinds) amount)
(list
(caadr kinds)0)
) kinds 1))
(defundd(
lists
kinds
counts) (
if(and(
<
(cadar lists)
(cadr
(assoc
(caar lists) kinds)))
(<
(cadadr lists)
(cadr
(assoc
(caadr lists) kinds)))
) counts
(if(>=
(cadadr lists)
(cadr
(assoc
(caadr lists) kinds)))
(and
(setf counts (
+ counts
(dd(
my-copylists((
cdr lists)
(list
(list
(find-after
(caadr lists) kinds)0)
))) kinds 0))
);;上面需要乙個額外的複製的函式,以防遞迴的程式修改原表(if
(<
(cadar lists)
(cadr
(assoc
(caar lists) kinds)))
counts
(and(
iter lists kinds)
(dd lists kinds (
1+ counts)))
))(and
(iter lists kinds)
(dd lists kinds (
1+ counts)))
)));;在(kinds)中找到大於給定幣值(num)的下乙個硬幣的面值
(defun
find-after
(num
kinds) (
if(null
(cdr kinds)
)nil(if
(= num (
caar kinds))(
caadr kinds)
(find-after num (
cdr kinds)))
));;我的定製的複製函式,可以返回乙個元素相同但互不相干的list
(defun
my-copylists
(lists) (
let((a
(caar lists))(
b(cadar lists))(
c(caadr lists))(
d(cadadr lists)))
(list
(list a b)
(list c d)))
);;繼續下一次的迭代
(defun
iter
(lists
kinds) (
and(
setf
(cadar lists)(-
(cadar lists)
(cadr
(assoc
(caar lists) kinds)))
)(set(
cadadr lists)(1+
(cadadr lists)))
))
先用書上原本的遞迴程式(已修改硬幣面額)進行試驗:
下面是我自己演算法的測試:
差不多到了5000時就像剛才程式的2000那般卡頓了
和原程式相比,我自己的演算法有不少侷限:
SICP 換零錢方法的統計
問題 現有半美元 四分之一美元 10美分 5美分和1美分共5種硬幣。若將1美元換成零錢,共有多少種不同方式?思路 採用遞迴過程,假定我們所考慮的可用硬幣型別種類排了某種順序,於是就有下面的關係 將總數為a的現金換成n中硬幣的不同方式的數目等於 注意這裡將換零錢分成兩組時所採用的方式,第一組裡面都沒有...
換零錢 見解
n元錢換為零錢,有多少不同的換法?幣值包括1 2 5分,1 2 5角,1 2 5 10 20 50 100元。例如 5分錢換為零錢,有以下4種換法 1 5個1分 2 1個2分3個1分 3 2個2分1個1分 4 1個5分 由於結果可能會很大,輸出mod 10 9 7的結果 input 輸入1個數n,n...
SICP中關於兌換零錢的練習
記得在sicp的第一章中,第1.2.2小節講樹形遞迴的時候,有乙個例項是換零錢方式的統計。其中,作者是用的樹形遞迴去求解的。但是,在這個例項的最後,作者又以找到乙個效率更高的演算法作為了乙個非正式的作業,留給了讀者作為挑戰。起先在看完這節內容的時候,也是絞盡腦汁都想不出還能有什麼更好的演算法。但是,...