SICP 換零錢方式的統計問題

2021-10-10 15:35:19 字數 3288 閱讀 8118

在《電腦程式的構造和解釋》(第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小節講樹形遞迴的時候,有乙個例項是換零錢方式的統計。其中,作者是用的樹形遞迴去求解的。但是,在這個例項的最後,作者又以找到乙個效率更高的演算法作為了乙個非正式的作業,留給了讀者作為挑戰。起先在看完這節內容的時候,也是絞盡腦汁都想不出還能有什麼更好的演算法。但是,...