《算法圖解》中動態規劃問題個例解析以及python編程實現

最近看了一本書,《算法圖解》,雖然內容不多,但是的確能把問題講得很清楚。
其第9章:動態規劃 就一些例子進行了很詳細的思路講解,但是沒有附帶代碼講解。
在這裏,根據該算法思路,對該書中的一個練習題進行編程解答。過程如下:

  1. 題目

在這裏插入圖片描述
根據作者思路,我在右側給出了答案,即最大價值爲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  #下一個物品出現的列 

運行結果如下:
在這裏插入圖片描述

總結:

  1. 需要在給定約束條件下優化某種指標時,動態規劃很有用;
  2. 問題可分解爲離散問題時,可使用動態規劃來解決;
  3. 每種動態規劃解決方案都涉及網格;
  4. 單元格中的值通常就是你要優化的值;
  5. 每個單元格都是一個子問題,因此你需要考慮如何將問題分解爲子問題。
  6. 沒有放之四海而皆準的計算動態規劃解決方案的公式。
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章