動態規劃(dynamic programming)與貪心演算法(greedy algorithm)

Britjeans發表於2018-06-07

《演算法導論》動態規劃筆記

動態規劃的方法是付出額外的記憶體空間來節省計算時間,是典型的時空權衡(time-memory trade-off)的例子。時間上的節省可能是非常巨大的,有可能將指數時間的解轉化為多項式時間的解。

應用動態規劃方法求解的優化問題應該具備的兩個要素:最優子結構和子問題重疊。

最優子結構

如果一個問題的最優解包含其子問題的最優解,我們就稱此問題具有最優子結構性質。

對於不同問題領域,最優子結構的不同體現在兩個方面:

  1. ·原問題的最優解涉及多少個子問題

  2. 在確定最優解使用那些子問題時,我們需要考察多少種選擇

最優子結構的性質:

問題的最優解由相關子問題的最優解組合而成,而這些子問題可以獨立求解

獨立求解也就是同一個原問題的一個子問題的解不影響另一個子問題的解。

例:無權最長路徑問題不滿足子問題的獨立性,因為在兩個子問題中可能會重複利用某些頂點。


動態規劃設計的一般步驟:

  1. 刻畫一個最優解的結構特徵

  2. 遞迴地定義最優解的值

  3. 計算最優解的值,通常採用自底向上的方法

  4. 利用計算出的資訊構造一個最優解


動態規劃有兩種等價的實現方法:

  1. 帶備忘的自頂向下法(top-down with memoization)。此方法按照自然的遞迴形式編寫,但是過程中會儲存每個子問題的解,通常存在一個陣列或者雜湊表中。當需要一個子問題的解的時候,過程首先你檢查是否已經儲存過這個解。如果是,就直接返回此儲存的值。

  2. 自底向上法(bottom-up method)。當求解某個子問題的時候,它所依賴的那些更小的子問題都已經求解完畢,結果已經儲存。每個子問題只需求解一次,當我們求解它時,它的所有前提子問題都已經求解完成。

由於沒有頻繁地遞迴函式呼叫的開銷,自底向上的方法比自頂向下法的時間開銷可能會小一些。


例題以及虛擬碼實現:

鋼條切割問題:給定一段長度為n英寸的規劃鋼條和一個價格表p,求切割鋼條的方案,使銷售收益r最大。

自頂向下版本

MEMOIZED-CUT-ROD(p,n)
    let r[0..n] be a new array
    for i=0 to n
    	r[i]=-oo
    return MEMOIZED-CUT-ROD-AUX
    
MEMOIZED-CUT-ROD-AUX(p,n,r)
	if r[n]>=0
		return r[n]
	if n==0
		q=0
	else q=-oo
		for i=1 to n
			q=max(q,p[i]+MEMOIZED-CUT-ROD-AUX(p,n-i,r))
	r[n]=q
	return q

自底而上版本

BOTTOM-UP-CUT-ROD(p,n)
	let r[0..n] be a new array
	r[0]=0
	for j=1 to n
		q=-oo
		for i= 1 to j
			q=max(q,p[i]+r[j-i])
		r[j]=q
	return r[n]
 


效能比較:

自底向上和自頂向下的演算法具有相同的漸進執行時間,均為theta(n^2)。

自底向上動態規劃演算法是按“逆拓撲序”(reverse topological sort)來處理子問題圖中的頂點。


《演算法導論》貪心演算法筆記

演算法設計

1. 將最優化問題轉化為這樣的形式:對其作出一次選擇後,隻身下一個子問題需要求解。

2. 證明作出貪心選擇後,原問題總是存在最優解,即貪心選擇總是安全的。

3. 證明作出貪心選擇後,剩餘的子問題滿足性質:其最優解與貪心選擇組合即可得到原問題的最優解,這樣就得到了最優子結構。


貪心選擇性質(greedy-choice property)

我們可以通過作出區域性最優選擇來構造全域性最優解。


貪心演算法與動態規劃的不同之處:

在動態規劃中,每個步驟都要進行一次選擇,但是選擇通常依賴於子問題的解。在貪心演算法中,我們直接作出在當前問題中看來最優的選擇,而不必考慮子問題的解。

例:

0-1揹包問題不具貪心選擇性質

分數揹包問題具有貪心選擇性質


相關文章