資料結構學習 遞迴與動態程式設計

2021-04-08 20:36:54 字數 2312 閱讀 5171

然後他給出了用陣列實現的乙個計算n個fibonacci數的非遞迴程式,接著說實際只需跟蹤2個值就可以了,因為最後得到f(n)只需前兩個值,那就正好跟我上面寫的非遞迴程式一致。接著他還提到了int 型也就是32位整數可以存放的最大fibonacci數為f(45)=1836311903然後我用自己的程式算得的是f(46)才是這個值,估計我和他數第1個不一樣,我以 1,1,2做為前3個,這個應該是沒錯的,然後我計算f(47)時就溢位成負數了,呵呵,我是拿非遞迴的那個版本計算的,然後我拿遞迴版本一算,哇靠,算個f(46)它竟然在我1.5ghz,256mb記憶體的機器上算了2分多鐘,期間因為遞迴的函式呼叫要儲存大量的資訊入棧導致記憶體不夠,頻繁交換頁面檔案,硬碟燈狂閃,cpu佔用率100%別的什麼都動不了。我暈死,先頭還沒拿老人家的話當回事,這下算是領教了。而我前一次用非遞迴求的時候一秒鐘估計都沒用到。

當然他要是沒有解釋遞迴在這兒低效的原因,那他算是枉做donald knuth的學生了,呵呵,原因是:

在將整個問題分解為兩個部分時,後一部分完全沒有注意到它要用到的資訊已經被計算出來了,而做了大量的重複計算。自己舉個比如f(5)畫出遞迴樹,一下子就看出來了。

指出了問題,解釋了原由,當然還要給出改進的方法,方法就是運用動態程式設計(我總覺得這個名詞用得有點誇張)

先給出fibonacci數列的動態程式設計解法:

int  f(int i)

用這個函式也跟非遞迴的版本一樣,f(46)瞬間搞定。

遞推是乙個有整數值的遞迴函式,我們可以按從最小開始之順序計算所有函式值來求任何類似函式的值,在每一步使用先前已計算的值計算當前值。我們稱這種方法為自底向上的動態程式設計。假設我們能夠儲存所有先前計算的值,那麼它能應用於任何遞迴計算。這是乙個演算法設計的技術,我們必須注意乙個簡單的技巧,以便能從指數級向線性改進乙個演算法的執行時間。

自頂向下的動態程式設計甚至是乙個更簡單的技巧,與自底向上的動態程式設計相比,它在同一(或更小)代價的基礎上自動允許我們實現遞迴函式。我們實現遞迴程式儲存每乙個它所計算的值(正如它最末的步驟)。上面的改進版fibonacci程式就是自頂向下的動態程式設計將執行時間減少為線性的例子。自頂向下的動態程式設計有時也稱為默記法(memoization)。

接下來他還舉了個更複雜一點的例子--揹包(kanpspack)問題,以前就總聽說這個概念,今天一看,其實也不是很複雜,但是很典型,總之應該是個最優化決策的問題。用遞迴實現群舉最終找出最佳策略。但這個演算法中也存在跟fibonacci數列相同的問題,當然同樣也可以用動態程式設計解決之。

揹包問題的簡單遞迴實現:

就如我們前面所說,千萬不要使用這個程式,因為它將花費指數級時間,即使是對於小問題也不能完成。但是它畢竟表明了一種我們可以輕易改進的緊湊方案。

先有包定義:

typedef structitem;

然後有乙個型別為item的n項的陣列,對於每乙個可能的項,我們(遞迴地)計算我們所能得到的最大值,包括那一項,然後挑出那些值中最大的一項。(m為包的大小)

int knap(int cap)                      //cap==m

}return max;

}揹包問題的動態程式設計實現:

由於我們簡單地儲存我們所計算的函式值,然後在我們需要的時候檢索已經儲存的值(使用乙個wentinel值來表達未知值),而不是製造遞迴呼叫。我們儲存資料項索引,以便我們能夠在計算之後重建揹包的內容,如果我們希望:itemknown[m]在揹包中,那麼剩下的內容就跟大小為m-known[m].size的最優的揹包的內容一致,因此itemknown[m-items[m].size]在揹包裡,以此類推:

int knap(int m)}}

maxknown[m]=max;

itemknown[m]=items[maxi];

return max;

}在自頂向下的動態程式設計中,我們儲存已知的值;在自底向上的動態程式設計中,我們預先計算這些值。我們通常選擇自頂向下的動態程式設計而不選擇自底向上的動態程式設計,其原因如下:

自頂向下的動態程式設計是乙個自然問題解決方案的機械轉換;

計算子問題的順序能自己處理;

我們或許不需要所有子問題的答案。

動態程式設計的應用程式在子問題的本質以及我們關於子問題的要儲存的資訊總量是不同的。

我們不能忽視的至關重要的一點是,當我們需要的可能的函式值的數目太高以至於不能儲存(自頂向下)或預處理(自底向上)所有值時,動態程式設計就會變得低效。比如,如果揹包問題中的m和資料項是64位或者是浮點數,我們將無法通過索引到陣列中去儲存值。這一差別不僅導致了小麻煩,還帶來了乙個主要困難。對這種問題,卻沒有好的辦法,以後會看到,的確是沒有好的辦法。

動態程式設計是乙個演算法設計技巧,基本適合我們以後要做的那些高階的排序,搜尋問題。無論如何,自頂向下的動態程式設計確實是開發高效的遞迴演算法實現的基本方法,這類演算法應該歸入任何從事演算法設計與實現所需的工具箱。 

資料結構學習筆記 遞迴

遞迴 乙個函式自己直接或間接呼叫自己。函式的呼叫 當在乙個函式的執行期間呼叫另乙個函式時,在執行被調函式之前,系統需要完成三間事 1 將所有的實際引數 返回位址 當被調函式完成後,接下來要執行的語句的位址 等資訊傳遞給被呼叫函式儲存。2 為被調函式的區域性變數 也包括形參 分配儲存空間。3 將控制轉...

資料結構學習筆記 遞迴 例項

part類用map存有孩子的類和資訊 數量 用string存有自己的名字name,如下 public class part 問題 通過成員函式count howmany part p 計算類物件擁有目標目標結點的數量 例如,howmany couch floor howmany couch hosp...

資料結構學習

什麼是資料結構 對計算機記憶體中的資料的一種安排。資料結構有那些?優缺點?1.陣列 插入快 知道下標 查詢慢,刪除慢,大小固定 2.有序陣列 比無序的查詢塊,刪除和插入慢,大小固定 3.棧 吃多了吐 個人理解 4.佇列 吃多了拉 個人理解 5.鍊錶 插入快,刪除快,查詢慢 6.二叉樹 查詢 插入 刪...