SICP中關於兌換零錢的練習

2021-09-26 21:56:34 字數 1657 閱讀 8889

記得在sicp的第一章中,第1.2.2小節講樹形遞迴的時候,有乙個例項是換零錢方式的統計。其中,作者是用的樹形遞迴去求解的。但是,在這個例項的最後,作者又以找到乙個效率更高的演算法作為了乙個非正式的作業,留給了讀者作為挑戰。起先在看完這節內容的時候,也是絞盡腦汁都想不出還能有什麼更好的演算法。但是,在看完了大半本書之後,無意中發現了一種方式。其實方法也很笨,就是列舉法,只是當時學到的語法太少了,不知道如何用scheme表達罷了,但是如果換成用python,那當時就應該能馬上寫出來的。好了 不廢話了,直接上**吧:

; 方法1 純函式式實現

(define (count-change-1 amount denomination-list)

(length (filter

(accumulate

(lambda (a-list b-list)

(flatmap

(lambda (a) (map (lambda (b) (cons a b)) b-list))

a-list))

(list '())

(map

(lambda (c) (enumerate-interval 0 c))

(map (lambda (d) (floor (/ amount d))) denomination-list))))))

這是用純函式式的程式設計風格實現的方法,還是那函式式程式設計的三大件:filter、map、accumulate。這裡沒有使用scheme自帶的reduce而是用了書上定義的accumulate是有原因的,因為accumulate是用遞迴實現的,而reduce我猜測mit-scheme是用迭代實現的,這裡用accumulate能保證對映後結果的次序問題,而一旦使用了reduce則保證不了,從而導致錯誤。其他的過程像enumerate-interval,flatmap等等都在之前的文章**現過了,書上也有,就不再放**了。

然後是使用非函式式程式設計對上面**的一點改進,效率會高一點。首先要定義乙個核心的過程,巢狀對映:

; 定義巢狀對映

(define (rec keys-list args)

(if (null? keys-list)

(lambda (key)

(rec (cdr keys-list) (cons key args)))

(car keys-list))))

(rec keys-list '()))

; 方法2 迴圈巢狀

(define (count-change-2 amount denomination-list)

(let ((count 0))

(define (count-inc! . b)

(set! count (+ count 1))))

(nested-map

for-each

count-inc!

(map

(lambda (c) (enumerate-interval 0 c))

(map (lambda (d) (floor (/ amount d))) denomination-list)))

count))

雖然效率有那麼一點提高,但是這裡使用了區域性變數,因此已經不再屬於函式式程式設計了。但是基本的思路還是沒改變:先列舉,再篩選。

力扣 兌換零錢

題目 給定不同面額的硬幣 coins 和乙個總金額 amount。編寫乙個函式來計算可以湊成總金額所需的最少的硬幣個數。如果沒有任何一種硬幣組合能組成總金額,返回 1 通過動態規劃來進行求解 首先可以將問題拆分成相等的子問題,假設dp n 等於構成n元的最少的硬幣數,那麼dp n dp n m dp...

SICP 換零錢方法的統計

問題 現有半美元 四分之一美元 10美分 5美分和1美分共5種硬幣。若將1美元換成零錢,共有多少種不同方式?思路 採用遞迴過程,假定我們所考慮的可用硬幣型別種類排了某種順序,於是就有下面的關係 將總數為a的現金換成n中硬幣的不同方式的數目等於 注意這裡將換零錢分成兩組時所採用的方式,第一組裡面都沒有...

SICP 換零錢方式的統計問題

在 電腦程式的構造和解釋 第2版 p26頁,作者用遞迴的方式為我們提供了一種解決思路 將總數為a的現金換成n種硬幣的不同方式的數目等於 下面是我將書中scheme的方言換成common lisp實現的 defun first denomination kinds of coins cond kind...