遞迴在實際程式設計中有著很重要的用處,最常見的用途莫過於檔案搜尋目錄遍歷了,我曾花了很長時間研究遞迴的性質,發現所有遞迴都可以用樹的結構描述出來,這只是我的發現,並沒有嚴格的證明,至少在我所研究過的所有遞迴應用例項中是成立的。
遞迴的效率顯然不是很高,因為每次遞迴的時候,呼叫函式時得分配棧空間,函式返回時,撤銷棧空間,所以,用能迴圈取代遞迴的,就盡量用迴圈實現。
一次我在街頭因好奇心強而與擺地攤的人下了一盤棋,很明顯是我輸了三十元,也惹得那時的女友大發脾氣。為此,我回家後潛心研究象棋程式設計,神奇的發現,強大的象棋引擎竟然只是乙個遞迴+評估函式,從此我迷上了遞迴,在這裡我把心得貼出來,作為筆記。
int minmax(int depth) else }
int max(int depth)
generatelegalmoves();
while (movesleft()) }
return best; }
int min(int depth)
generatelegalmoves();
while (movesleft()) }
return best; }
上面的**可以這樣呼叫:
val = minmax(5);
這樣可以返回當前局面的評價,它是向前看5步的結果。
我簡要描述一下這個函式是如何運作的。假設根局面(棋盤上當前局面)是白方走,那麼呼叫的是「max」函式,它產生白方所有合理著法。在每個後續局面中,呼叫的是「min」函式,它對局面作出評估並返回。由於現在是白走,因此白方需要讓評估盡可能地大,能得到最大值的那個著法被認為是最好的,因此返回這個著法的評估。「min」函式正好相反,當黑方走時呼叫「min」函式,而黑方需要盡可能地小,因此選擇能得到最小值的那個著法。這兩個函式是互相遞迴的,即它們互相呼叫,直到達到所需要的深度為止。當函式到達最底層時,它們就返回「evaluate」函式的值。如果在深度為1時呼叫「minmax」函式,那麼「evaluate」函式在走完每個合理著法之後就呼叫,選擇乙個能達到最佳值的那個著法導致的局面。如果層數大於1,那麼另一方有權選擇局面,並找乙個最好的。
以上內容應該不難理解,但是**很長,下面有個更好的辦法。
負值最大函式
負值最大只是對最小-最大的優化,「評估」函式返回我所說的第二種定義,對於當前結點上要走的一方,佔優的情況返回正值,其他結點也是對於要走的一方而言的。這個值返回後要加上負號,因為返回以後就是對另一方而言了。**如下:
int negamax(int depth)
generatelegalmoves();
while (movesleft()) }
return best; }
在這個函式裡,當走子一方改變時就要對返回值取負值,以反映當前局面評估的更改。就根結點是白先走的情況,如果沒有剩下的層數,那麼「評估」返回的值是就白方而言的,如果有剩下的層數,就產生後續局面,函式對這些局面逐一做遞迴,每個次遞迴都得到就黑方而言的評估,黑方走得越好值就越大。當評估值返回時,它們被取負數,變成就白方而言的評估。
該函式在遍歷時結點的順序同「最小-最大」搜尋的函式是一樣的,產生的返回值也一樣。它的**更短,同時減少了移植**時出錯的可能,**維護起來也比較方便。
我所了解的遞迴
所謂遞迴,就是當你需要完成某個功能時發現下一層次的需求跟上一層次的需求相同,相同到如果完成整個功能可能需要寫無數相同 段 這個時候我們可以不可以使用while迴圈或者多層迴圈巢狀呢,可以,但是它並不能完美解決所有要求,貼出 使用該字典根據輸入要查詢的城市 輸出他的二級城市或地點 menu 網易 go...
我所理解的前端
轉眼間,在鵝廠的實習已經過去三個多月,涉及到實習生轉正留用的考核流程也逐步開始了。帶著一堆疑問,以及自己實習期間的心得體會,與導師暢談了一番。他作為資深前端工程師,就前端領域及我個人未來的職業規劃等方面分享了他自己的經驗。這次與導師的溝通讓我受益匪淺,現簡單總結如下。前端知識學習路線 首先,當然是就...
我所理解的陣列
陣列 一 一維陣列 1 陣列的建立 陣列顧名思義是含有相同元素的集合,類似我們高中數學所學習的集合 例如int arr 10 char arr1 2 float arr2 3 double arr3 5 注意 切記 這個中要給常量,不能使用變數。2 陣列的初始化 初始化是指 在陣列的建立同時並賦予合...