從買書那天算起,到今天已經過了半個多月。這段時間說短不短,如果是一本
300多頁的**的話,我大概一天就能搞定(我的記錄是一天一千多頁《大唐雙龍傳》),但是到現在《程式設計之美》我只看了不到
50頁。雖然我不是天天看,但是一旦我看了乙個問題之後,我就希望能夠把這個問題在演算法層面分析透,這份專注是我以前看《演算法導論》或者上演算法課的時候所不曾體會到的。究其原因,主要還是純粹的理論流於枯燥,純粹的應用不免膚淺,而這本書的定位剛剛好,既能夠以應用帶動演算法的學習,又能夠避免過於說教的風格。
儘管初衷不錯,但我仍覺拿捏得尺度有待商榷。因為程式設計應該既嚴謹又靈活,嚴謹的思考保證了程式執行的穩定,而靈活的思維則為創新提供了條件。而「程式設計之美」給我的感覺是靈活有餘嚴謹不足。我覺得既然是有關演算法的書,那麼在一些關鍵點上的證明就不可或缺,例如在我看的六個問題中就出現兩個貪心演算法沒證明其貪心選擇性,或討論的不夠,其中「買書問題」的貪心選擇還是有問題的。當然,我們不應該奢求這本定位於「面試題集」的書能夠寫出如「演算法導論」般嚴格的證明。但至少應該給出具體思路,以證明想法是有依據而非直觀感覺(因為我們的直觀感覺在很多時候是不準確的,就比如我的讀書筆記四種分析買書問題,如果把三本書的折扣改成
15%,那我們就有可能被得到的折扣**所誤導而採用原始的貪心演算法);最可怕的是鑑於構建反例實際是一件非常困難的事情,所以我們從給出的例子中可能也無法看出端倪。此外,在看書過程中所遇到的各種錯誤的數量也是屢創新高。聽說馬上就要出第二版了,真是為後面的內容擔心,難道最後又要附一頁勘誤表?
雖然問題多多,但由於瑕不掩瑜,我們仍然能夠從書中獲得足夠多的微軟人的智慧型,哪怕是促使我們重新溫習一遍演算法基礎也好,希望第四版(
如果有的話
:-)
)能看到乙個完美的「程式設計之美」。
本題所說的問題是微軟每天為員工提供各種不同的飲料,如可樂,酸奶,豆漿,咖啡,綠茶
……..
(待遇不錯啊,呵呵),飲料
i的單位容量為
vi,其中每種飲料單個容量都是
2的方冪,員工對飲料
i的滿意度為
hi,冰櫃的總容量為
v(每天必須裝滿),問題是如何組合現有的各種飲料,使總的滿意度最高。
還是說一下我的第一印象,很顯然這是一道最優化問題,但很容易想到這道題和線性規劃的描述很符合,但是由於解線性規劃的單純型方法比較複雜,這裡就不再討論了。其次,回想一下經典的
0-1揹包問題,和本問題也有些相似,所不同的是
0-1揹包問題中,每件物品只能拿一次,而這裡同一種飲料能拿多瓶;此外,原問題中每天**的總量
v是必須達到的(否則會有員工投訴?),所以不能夠像
0-1揹包問題那樣有讓揹包裝不滿的情況。這個條件實際上改變了我們對於最優解的搜尋策略,因為容量為
v的裝飲料的冰櫃每天早晨都必須是滿的,所以即便有另乙個使滿意度最高但冰櫃不滿的組合我們也是不能選的。
其實我們可以稍微改變一下本題的條件,忽略原問題中的每種飲料單個容量都是
2的方冪的條件,並允許冰櫃不滿的情況下求最大滿意度的組合,希望可以使原問題的解決更富有一般性。
沒有懸念,優化問題就用動態規劃、貪心演算法、分支限界輪番上陣就好了。設
opt(v』,
i)表示從i到
n-1種飲料中,
ci為第
i種飲料可能的最大數量,算出總量為
v』的方案中滿意度之和的最大值。那麼遞迴式就應該是:
opt(v』,
i)=max
(k=0,1
,2…,ci
,i=0,1
,2…,n-1
)這裡我覺得需要說明給出的飲料組合最終可以組合出v。
書中的貪心解法似曾相識,把資訊按照飲料的容量排序(其中設我們有
n0種容量為
20的飲料):
volume
totalcount
20 tc0_0
h0_0
… …
…20 tc0_1
h0_n0
… …
…21 tc1_0
h0_0
…
…
2m
tcm_0
hm_0
…
然後按照下面的順序進行貪心選擇:
(1)飲料總量為
1,從容量為
20的飲料中選出快樂指數最大的。
(2)飲料總量為
2,從容量為
21的飲料中選出快樂指數最大的(設為
h1),與容量為
20的飲料中快樂指數最大的(設為
h0),比較h1和
2* h0
,取出其中最大者為當前最佳選擇
(3)繼續進行下去,直到求出
opt(v,
0)粗看一下,有些似曾相識。我們在買書問題的時候也曾面臨將買書計畫拆分,然後查表進行貪心選擇。然而買書問題每次選擇至多選
m本書,而且每次選擇只影響下一次選擇,所以只需要把
2m進行有限的拆分即可。
而本題則不盡相同,對於某種容量
v』(以
v』=11
為例)來說,有兩個問題:
1.首先,我們需要察看所有拆分的可能性,找出其中最大者作為本次貪心選擇的結果。其中,由於「每種飲料的單位容量都是
2的方冪」,所以拆分結果僅考慮用小於v』的
2的方冪來進行組合,即(計算式1
)
11=8+2+1
,4+4+2=1
,4+2+2+2+1,….
,1+1+…+1
。可以看到,對於
v』我們至少可以「拆」出
v』種組合(或許更多),即便我們把每次的計算結果用**儲存起來,我們的查詢次數也至少是ω(
1+2+…+v』=v』2
),空間複雜度也很高,並沒有如數中說「簡單很多」。而且,如果取消「每種飲料的單位容量都是
2的方冪」的限制,拆分的結果就會變成(計算式2
)
11=10+1
,9+2,…
,5+5
,5+5+1
,5+4+2
,…..
,導致查詢次數進一步增加。
2.其次,讓我們回到貪心選擇的定義上來。由於貪心選擇性,貪心演算法的過程是每次選擇都選取當前看來最好的結果,使得當達到最終狀態時的結果剛好是最優解。而我們再看
v』=11
的例子,假設最優解是
4+4+2+1
,則我們曾經計算過的
8就不是最優解的一部分,這就和貪心演算法的精神不符,所以這個方法其實還是動態規劃。
動態規劃解法很好,貪心演算法有待商榷。其實這是正常的,因為通常情況下使用貪心演算法的難點在於證明貪心選擇性的存在,鑑於這本書的定位,我們不能苛責更多。但是這個問題和上乙個問題(買書折扣問題)的貪心演算法我覺得都過於草率。如果想讓這本書成為經典,「微軟面試」的噱頭是遠遠不夠的,精益求精的態度至關重要,有時候真的沒必要把
deadline
設定的那麼緊迫,偶爾跳票追求質量也是理所應當,看看人家暴雪的「星際爭霸
2」跳票這麼多年就知道了
…….[1] p41
,第七段,「
opt(v』,
i)表示從第i,
i+1,…,
n-1,
n種飲料
…」,應該把
n去掉,因為
i的取值範圍是[0,
n-1]
。[2] p41
,第八段,「
opt(v,
n)」應該改為
opt(v,
0)。[3] p41
,推導公式,應改為
max(…,
i=n-2
,n-1,……
,0)[4] p42
,**清單
1-9,
for (int i=0; i <= v; i++)
,應改為
for (int i=1; i <= v; i++)
,因為opt[v][n]
的第一行都是
0,就不用計算了。
[5] p42
,**清單
1-9,「
if ( i<=k*v[j] )
」應該改為
if ( i < k*v[j] )
,因為顯然等號情況應該保留
[6] p42
,最後一段,應改為計算
opt[i][j]
時只需要
opt[k][j+1](0<=k<=v)
這一列,所以不必列出整個矩陣,而只需要兩列即可。
[7] p44
,解法三種的表的第三行,
tc0_1
,應該改為
tc0_n0
《程式設計之美》讀書筆記
程式設計之美 讀書筆記 一 中國象棋將帥問題 程式設計之美 讀書筆記 二 求二進位制數中1的個數 擴充套件問題 程式設計之美 讀書筆記 三 一摞烙餅的排序問題 程式設計之美 讀書筆記 四 買書折扣問題的貪心解法 程式設計之美 讀書筆記 五 飲料 問題 程式設計之美 讀書筆記 六 連連看遊戲設計 程式...
《程式設計之美》讀書筆記集錦
程式設計之美 讀書筆記 一 中國象棋將帥問題 程式設計之美 讀書筆記 二 求二進位制數中1的個數 擴充套件問題 程式設計之美 讀書筆記 三 一摞烙餅的排序問題 程式設計之美 讀書筆記 四 買書折扣問題的貪心解法 程式設計之美 讀書筆記 五 飲料 問題 程式設計之美 讀書筆記 六 連連看遊戲設計 程式...
《程式設計之美》讀書筆記集錦
程式設計之美 讀書筆記 一 中國象棋將帥問題 程式設計之美 讀書筆記 二 求二進位制數中1的個數 擴充套件問題 程式設計之美 讀書筆記 三 一摞烙餅的排序問題 程式設計之美 讀書筆記 四 買書折扣問題的貪心解法 程式設計之美 讀書筆記 五 飲料 問題 程式設計之美 讀書筆記 六 連連看遊戲設計 程式...