《程式設計之美》讀書筆記(一):中國象棋將帥問題
千呼萬喚始出來,在跳票了快乙個月之後,雖然明知道書裡還有不少錯誤沒改過來(附了一整頁的勘誤),但是感覺已經不能等下一版了。趕快去書店買回來,吃完飯躺床上舒舒服服地看。大致翻看之後,總體感覺是書中的內容沒有「脫離群眾」,很多都是我們平時生活、工作中經常能遇到的。題目不見得難,基本上給一本《演算法導論》和足夠的時間,大多數人都能解決其中的問題。但注意副標題--「微軟技術面試心得」,這就給這本書定下乙個基調:面對這些我們並不陌生、也並非特別困難的問題,在有限的時間裡,(可能)比較緊張的心情之下,如何充分發揮自己分析問題和解決問題的能力,如何正確且漂亮地解決問題才是關鍵。我想,在平時學習的時候或許我們左手《演算法導論》,右手《程式設計之美》效果會更好一些。
中國象棋將帥問題由於比較簡單,所以我們暫時不用請出《演算法導論》。該問題的具體描述是:(根據中國象棋的基本原則)在只有雙的將帥棋盤上,找出所有雙方可以落子的位置(將帥不能碰面),但只能使用乙個變數。直覺上我們想到,只要遍歷將帥所有可能的位置,去除將帥衝突的位置即可。可見,剩下的問題就在於如何使用乙個變數來做二重迴圈的遍歷。書中解法一給出的方法是將乙個byte變數拆成兩個用,前一半代表「帥」可以走的位置,後乙個變數代表「將」可以走的位置(事先已經將「將」和「帥」可以走的3*3的位置進行了編號),利用位操作即可獲得兩個計數器的功能。書中的解法三採用結構體來解決乙個變數遍歷二重迴圈的問題,思想上換湯不換藥。真正有趣的是解法二,它的**如下:
int var = 81;
while( var-- )
當看到這個解法的時候,我心裡有一些感慨。在前幾個月,我一直未msra面試沒通過而惱火。但看到這個解法之後,我覺得我確實還要再努力一些才行。短短幾行,體現了簡約之美,僅看看這個就值回錢了(開玩笑)。雖然可能有牛人說這沒什麼了不起,但我覺得如果我在面試這個問題的時候能寫下這樣的**,我會很有成就感。在大多數時候我們無需知道希爾排序的時間複雜度的一點幾次方是怎麼算出來的,也無需去證明乙個最優化問題是否滿足「擬陣」的條件,我們只需要在這樣乙個「簡單」的問題上做得漂亮,就夠了。
回過頭來分析這個解法。「將」和「帥」各在自己的3*3的格仔間裡面走動,我們共需要驗證9*9=81種位置關係,這也是i=81的由來。此外我們要明白i/9和i%9的含義。我們知道,整數i可以由部兩分組成,即var=(var/9)*9+var%9 ,其中var簡單即是美,相對於解法一的大段**,我更希望我以後再面試中寫出解法二。
其實這個問題還可以進行一些擴充套件,即如何利用乙個變數達到三重迴圈的效果。也就是說,如果給定下面的迴圈:
int counter = 0;
for( int i = 0; i < 5; i++ )
for( int j = 0; j < 4; j++ )
for( int k = 0; k < 3; k++ )
其結果如下:
counter=0 , i=0, j=0, k=0
counter=1 , i=0, j=0, k=1
counter=2 , i=0, j=0, k=2
counter=3 , i=0, j=1, k=0
counter=4 , i=0, j=1, k=1
....中間略
counter=59 , i=4, j=3, k=2
問題是(1)我們如何用乙個列印出相同的結果?(2)如果是n重迴圈呢?
面對第乙個問題,實際上就是對原始的中國象棋將帥問題進行了乙個擴充套件,即在棋盤上新增乙個「王」,其行走規則和將帥 一樣。於是棋盤變成了三國爭霸:-) ,將帥王可以走動的格仔數分別為3、4、5,它們之間的互斥條件可以按需要設定。
這時,就需要只用乙個變數遍歷乙個三重迴圈。直觀的方法是像方法一那樣把乙個4位元組的int拆開來用。我這裡只關注方法二。
只用乙個變數解決擴充套件的中國象棋將帥問題,我們的**應該是如下的樣子:
int var = 3*4*5;
while( var-- )
在衝突條件中,我們需要知道var取得某個特定的值(即第var+1次迴圈)的時候的i,j,k分別是多少(這樣我們才能判定將帥位置是否衝突)
從上例的結果中我們可以看到,counter的值(即當前的迴圈次數)和三元組(i,j,k)是一一對應的,越是外層的迴圈變化越慢,他們滿足什麼關係呢?
k的取值最好確定,我們都知道是var%3。
在原始的將帥問題中我們知道,j的值應該是 var/3,但是由於j上面還有一層迴圈,就需要做些調整,變成var/3%4
最外層迴圈i的值則為(var/(3*4))%5.
即:k=var%3 //其下沒有迴圈了
j=var/3 //其下有幾個迴圈長度為3的迴圈
i=var/(3*4). //其下有幾個迴圈長度為3*4的迴圈
於是4重迴圈的公式我們也可以輕鬆得出:
for( int i = 0; i < 5; i++ )
for( int j = 0; j < 4; j++ )
for( int k = 0; k < 3; k++ )
for( int p = 0; p < 3; p++ )
p=var%2 //其下沒有迴圈了
k=var/2 //其下有幾個迴圈長度為2的迴圈
j=var/(2*3)) //其下有幾個迴圈長度為2*3的迴圈
i=var/(2*3*4)//其下有幾個迴圈長度2*3*4的迴圈
下面就是乙個變數實現三重迴圈
var =2
*3*4*5;
while
( var
-->0)
結果是:
var=119 , i=4, j=3, k=2, p=1
var=118 , i=4, j=3, k=2, p=0
var=117 , i=4, j=3, k=1, p=1
...中間略
var=5 , i=0, j=0, k=2, p=1
var=4 , i=0, j=0, k=2, p=0
var=3 , i=0, j=0, k=1, p=1
var=2 , i=0, j=0, k=1, p=0
var=1 , i=0, j=0, k=0, p=1
var=0 , i=0, j=0, k=0, p=0
n重迴圈原理也是一樣,就不再贅述了。
ps:看到最後一例的結果是不是與《演算法導論》中平攤分析一章的二進位制計數器很像?只不過這裡進製不一樣而已:-)
[勘誤: p19 **清單1-7的第七行,應該改為if(i.a%3 != i.b%3)]
謹以此文與大家共勉 2008/04/05
《程式設計之美》讀書筆記 一 中國象棋將帥問題
當看到這個解法的時候,我心裡有一些感慨。在前幾個月,我一直未msra面試沒通過而惱火。但看到這個解法之後,我覺得我確實還要再努力一些才行。短短幾 行,體現了簡約之美,僅看看這個就值回錢了 開玩笑 雖然可能有牛人說這沒什麼了不起,但我覺得如果我在面試這個問題的時候能寫下這樣的 我會很有 成就感。在大多...
《程式設計之美》讀書筆記(一) 中國象棋將帥問題
千呼萬喚始出來,在跳票了快乙個月之後,雖然明知道書裡還有不少錯誤沒改過來 附了一整頁的勘誤 但是感覺已經不能等下一版了。趕快去書店買回來,吃完飯躺床上舒舒服服地看。大致翻看之後,總體感覺是書中的內容沒有 脫離群眾 很多都是我們平時生活 工作中經常能遇到的。題目不見得難,基本上給一本 演算法導論 和足...
程式設計之美讀書筆記 1 2 中國象棋將帥問題
中國象棋將帥問題 在一把象棋的殘局中,象棋雙方的將帥不可以相見,即不可以在中間沒有其他棋子的情況下在同一列出現。而將 帥各被限制在己方的3 3的格仔中運動。相信大家都非常熟悉象棋的玩法吧,這裡就不詳細說明遊戲規則了。用a b代表將和帥,請寫出乙個程式,輸出a b所有合法的位置。要求在 中只能用乙個變...