最近總是跟動態規劃打交道,索性就將其弄明白一點。所謂的動態規劃並不是一種具體的演算法,而是一種演算法的思想。思想總是抽象的,縹緲的。所以在學習動態規劃的時候會有一種很尷尬的情況,就是感覺這個東西很簡單,但是又不知道自己是不是真的學會了。
簡單的來說,動態規劃就是將乙個大問題分解為子問題,然後根據子問題的解求出原問題的解。它適用於有重複子問題(在問題計算的過程中,子問題要被重複計算多次)和最優子結構(區域性最優解能決定全域性最優解)的問題。
根據問題的這兩個特徵,我們很容易去簡化這類問題:1.重複子問題:將子問題的結果進行儲存,下次使用的時候直接呼叫,而不是每次都重新計算一遍。 2.最優子結構: 按順序計算子問題的解,並且推導出子問題和下乙個子問題之間的狀態轉移方程。隨著最後乙個子問題的解決,整體的問題也就解決了。
這就是動態規劃的思想了,其實也就是針對某一類特定的問題,得到簡化這類問題的辦法。我們之前聊過的前向後向演算法,維特比演算法,ctc損失等都是這類思想的實際應用。當然,如何將思想在實地應用,還需要更多的努力。目前我還只是去學習別人在特定問題上應用的套路而已。
這是乙個簡單的例子,
斐波那契數列:0, 1, 1, 2, 3, 5, 8, 13, 21, 34, 55, 89, 144, 233……
它遵循這樣乙個規律:每一數都是前面兩個數的和
我們可以使用傳統的遞迴的方式來實現,但是其複雜度是指數級的,我們知道指數級就相當於是不可用。實際測試了一下,計算100的斐波拉契數列,那個慢呀?唐僧也會愁到罵娘的。
我們使用動態規劃的思想分析一下問題,首先計算每一數的時候,前面所有的數都得重新計算一下,符合重疊子問題。其次最終問題的最優解包含的前面的數也是最優解,符合最優子結構。所以可以使用動態規劃的思想來解決。
1.建立狀態轉移方程:results[i] = results[i-1]+results[i-2]
2.使用results 儲存已解決子問題的解
3.從小到大的順序計算每乙個值
整體的複雜度為o(n
)o(n)
o(n)
,對比原來的o(2
n)o(2^n)
o(2n
)來說就是天壤之別,到這裡就清爽了很多。
#簡單遞迴 複雜度為指數級
deffib
(n):
if n<2:
return n
else
:return fib(n-1)
+ fib(n-2)
#使用動態規劃 儲存子問題的值
deffib2
(n):
results =
list
(range
(n+1))
for i in
range
(n+1):
if i <2:
results[i]
= i else
: results[i]
= results[i-1]
+results[i-2]
return results
import datetime
if __name__ ==
'__main__'
: start = datetime.datetime.now(
)print
(fib(
100)
) end = datetime.datetime.now(
)print
(end - start)
我們介紹了動態規劃的思想,主要用來解決有重複子問題和最優子結構的問題。具體分為三步,一建立狀態轉移方程 二儲存已解決子問題的解,重複利用 三 按順序從小到大 計算每個子問題。動態規劃應用非常廣泛,如前向後向演算法、維特比演算法、揹包問題、最長子串問題、最長子序列問題等。還需要是實際生活中多觀察、多思考,才能完全的領會這種思想的精髓。
貪婪演算法和動態規劃有一些相似的地方,比如其都需要把大問題分解為子問題,由區域性最優推出全域性最優。不一樣的地方在於貪婪演算法不會儲存子問題的值,因此其也就沒有會退的功能。而且使用貪婪演算法得到的一般是近似解,而不是最優解,因為貪婪演算法並不會去驗證所有的可能性。
比如揹包問題,小偷揹著乙個能裝4磅的包,面前有三種物品 音響 4磅 3000$;吉他 1磅 1500元;膝上型電腦 3磅 2000元;小偷如何使得包中所裝物品的總價值最高(小偷也太難了)?
如果是乙個傻小偷,隨便那幾件直到裝不下;如果是乙個稍微有腦子的小偷,就算一算,所有可能的情況,選出乙個最好的。這就是窮舉法,適合物品較少的情況。如果有100件商品,估計小偷沒有算出來就已經進去了。
所以我們需要求的是針對這類問題的一種方**級別的策略,有乙個聰明的小偷,它每次都選擇所有物品中最貴的來偷,直到裝不下。這就是貪婪演算法。
還有乙個小偷,人稱小偷界的華羅庚。他使用動態規劃演算法,迅速的在心裡面畫這樣乙個圖。從第乙個單元格開始填,每個單元格的值為:
m ax
(當前商
品價值+
cell
[i−1
][j−
當前物品
質量],
cell
[i−1
][j]
)max(當前商品價值+cell[i-1][j-當前物品質量],cell[i-1][j])
max(當前
商品價值
+cel
l[i−
1][j
−當前物
品質量]
,cel
l[i−
1][j
])以這樣的方式求出了最高的價值,我已經不由得在心裡面豎起了大拇指。這應該就是傳說中的盜亦有道吧。
我們來總結一下貪婪演算法:
1.將原問題分解為多個子問題
2.求出每乙個子問題的最優解
3.將子問題的最優解進行合併,成為原問題的最優解。
它經常用在一些沒有辦法求得最優解的情況下,使用貪婪演算法可以得到近似最優解的解。
乙個人的時候,我喜歡做這樣的遊戲,懷疑自己的思想。我並不是像那些瘋子一樣,覺得這個世界有什麼奇怪的,說不通的地方。雖然說現實系統確實有很多比較明顯的bug,但是我才懶得去管它們呢,我只要開開心心的活著就行了。我所說的懷疑純粹是一種樂趣,就是為了懷疑而懷疑。因為我受不了被一種思維方式束縛的感覺,從我出生起就毫無反抗的接受了的這種思維。這麼多年讓我受盡折磨,奇怪的是我居然一直都沒有發現它才是罪魁禍首。異想天開在我這裡其實是個褒義詞,你就把天給想開了也沒什麼大不了的,就算它實際上沒有開於你也沒有什麼損失。我喜歡對著鏡子中那張英俊的臉思考,我怎麼會站在這裡?一切為什麼會是現在這個樣子的?毫無疑問,這是一種天理執行的規則,我只是規則下的產物而已。可是我為什麼會懷疑呢?我用思想去懷疑思想本身,就像用360去解除安裝360一樣,這說不通呀!有一些可信的猜想,這種懷疑本就是思想的一部分,思想的本質就是摧毀並且建造,要想練就這種絕世武功,它就得有掃平一切的力量,包括掃平自己。或者說思想本身就不屬於這個世界,身體只是它和這個世界對話的工具而已,這樣它就不用掃平自己了。可是不管怎麼樣,你發現沒?我都好像站在乙個圈內在思考,即使我從這個圈裡面跳出去還會有更大的圈。我們覺得無比自由的思想其實依賴於這個圈的限制,除非有一天這思想沒了,也許這就是老子的道吧!但是,這件事還是等到我老了再去幹,我可不願意這麼高的顏值隨著現有規則的摧毀就被浪費掉(看到沒,其實這也是它的狡猾之處)。
【heize吧中字】[unpretty rapstar2] semi final「不要掙錢」- heize(feat.燦烈)
分治 動態規劃 貪婪 之 演算法分析
分治與動態規劃都用到了遞迴的思想,但是對他們之間的區別在概念上一直比較模糊,今天附帶貪婪選擇稍微整理一下他們。演算法之道上說,標準分治 動態規劃 貪婪選擇稱得上是孫子兵法的下 中 上策。標準分治 雖然將大問題分解成小問題,但是每個小問題都需要解決,相當於逢城必攻實屬下策 動態規劃 則聰明地發現,很多...
演算法設計思想之「動態規劃」
關鍵區別 典例代表 動態規劃 子問題相互重疊 斐波那契數列 分而治之 子問題獨立 翻轉二叉樹 1.1 題目描述 1.2 解題思路 輸入 n 3 輸出 3 解釋 有3種方法可以爬到樓頂 1 階 1 階 1階 2 階 1 階 1 階 2 階 1.3 解題步驟 空間複雜度 o n function cli...
演算法基本思想之動態規劃
動態規劃 談到演算法最優解 則首先會想到兩個演算法第乙個是貪心演算法,其次才會是動態規劃 再者從問題解決規模的相似程度來看,可能會引出另外的乙個演算法叫做 分治法 更準確的來講叫做分治思想 有乙個問題是 現在假定學校要從計算機工程系選出本年度的三好學生,假定其名額一共有三個,其中學校計算機工程系的學...