最近看了一本書,《算法圖解》,雖然內容不多,但是的確能把問題講得很清楚。
其第9章:動態規劃 就一些例子進行了很詳細的思路講解,但是沒有附帶代碼講解。
在這裏,根據該算法思路,對該書中的一個練習題進行編程解答。過程如下:
- 題目
根據作者思路,我在右側給出了答案,即最大價值爲25.
圖中列上的數字1~6表示,當該揹包容量只有1-6磅時,行表示可以允許放置的物品(包括之前行的物品)。如 (3,2)處的9表示,在揹包只有2磅容量時,如果可以任意組合放入水、書、和食物,那麼能放入的最大價值爲9.
迭代公式如下:
代碼如下:
import numpy as np
#建立物品價格字典
prices={}
prices['water']=10
prices['book']=3
prices['food']=9
prices['jacket']=5
prices['camera']=6
#建立物品重量字典
pounds={}
pounds['water']=3
pounds['book']=1
pounds['food']=2
pounds['jacket']=2
pounds['camera']=1
total_pounds=6 #該揹包最大容量
#根據揹包重量(6)和最輕物體的重量(1)來確定多少列網格。
#並考慮到在迭代時需要考慮前一列的值,因此列數加1(但是默認第一列全爲0)
grids_num=total_pounds//pounds[min(pounds)]+1
things_items=list(prices.keys()) #將物品名單以列表形式展示
#定義一個5*7的全0矩陣,其中5表示5個物品,7表示1+6列格點
total_values=np.zeros((len(things_items),grids_num),dtype=int)
for i, item in enumerate(things_items):
price=prices[item] #獲取對應行的物品的價格
pound=pounds[item] #獲取對應列的物品的重量
if i==0: #如果是第一行
#由於只能放這個物品,當揹包空間小於該物品重量時,默認價值爲0,
#當揹包空間大於等於該物品時,只能放一次該物品,因此價值就是該物品
total_values[0,pound:]=price
else: #當不是第一行時,即可以開始多個物品組合放置時,需要進行迭代
for j in range(grids_num):
if j <pound: #當揹包空間小於物品重量時,默認使用其上一行的值
total_values[i,j]=total_values[i-1,j]
else: #當揹包空間大於等於該物品重量時,需要比較,取最大值
total_values[i,j]=max(total_values[i-1,j],
price+total_values[i-1,j-pound])
##通過以上程序可以獲取該揹包能裝的物品的最大價值和
運行結果如下:從左向右,從上到下,都是遞增的
下面來確定,哪些物品的組合能使得揹包所裝東西價值最大。
思路分析:
a. 每一個格點的值=max(上一行該格點的值,當前商品價值+剩餘空間價值)。
當取後者時候,說明,取當前商品是更有效的。如果取前者,說明,當前商品不必要 取,因爲不能帶來效益增長。
b. 如同,第6列,25>22,說明應該取相機,相機1磅,那麼便看剩下的5磅的最優組合。
c. 第5列,5磅空間下,取夾克沒有增益,19>13,即取食物有增益。食物佔據2磅,那麼接下來看剩下的3磅的最優組合。
d. 此時剩下3磅空間,且只能在水和書之間進行組合。看到第三列從水到書並沒有增益,說明取水是有效的。
因此類推。
##以下程序來獲取,當揹包裝了最大價值的物品時,到底是哪些物品。
def get_row_index(data):
'''
獲取一列或者一行數據中,最大值首先出現的位置索引
'''
data=list(data)
max_value=max(data)
index=data.index(max_value)
return index
#由迭代算法可知,每一列都是非遞減的,因此最後一行最後一列的數值一定爲最大值
max_values=max(total_values[:,-1])
#建立最大值物品的組合清單
things_needed=[]
i=len(things_items) #確定行數
j=grids_num-1 #確定列號
#由於總價值是由於多個物品組合的,下面循環用來確定哪些物品。
#每確定一個物品,便將總價值-該物品價值,直到找到該組合所有物品,總價值就爲0了
while max_values: #只要總價值不爲0
index=get_row_index(total_values[0:i,j]) #獲取該列最大值首先出現的位置索引
thing=things_items[index] #物品名稱
things_needed.append(thing)
price=prices[thing] #物品價格
pound=pounds[thing] #物體重量
max_values=max_values-price #減去該物品的價值
i=index-1 #下一個物品所在的行範圍: 0-i
j=j-pound #下一個物品出現的列
運行結果如下:
總結:
- 需要在給定約束條件下優化某種指標時,動態規劃很有用;
- 問題可分解爲離散問題時,可使用動態規劃來解決;
- 每種動態規劃解決方案都涉及網格;
- 單元格中的值通常就是你要優化的值;
- 每個單元格都是一個子問題,因此你需要考慮如何將問題分解爲子問題。
- 沒有放之四海而皆準的計算動態規劃解決方案的公式。