【算法分析】實驗 3. 基於動態規劃方法求解0-1揹包問題

實驗內容

    本實驗要求基於算法設計與分析的通常過程(即待求解問題的描述、算法設計、算法描述、算法正確性證實、算法分析、算法實現與測試),在針對0-1揹包問題求解的實踐中理解動態規劃 (Dynamic Programming, DP) 方法的思想、求解策略及步驟。算法

    做爲挑戰:能夠考慮基於跳躍點的改進算法,以及對連續型物品重量/揹包容量的支持。數組

實驗目的

  • 理解動態規劃方法的核心思想以及動態規劃方法的求解過程;
  • 從算法分析與設計的角度,對0-1揹包問題的基於DP法求解有更進一步的理解。

實驗結果

步驟1

    理解問題,給出問題的描述。數據結構

    n個物體,1個揹包。對物品i,其價值爲\(v_i\),重量爲\(W_i\),揹包的容量爲 \(W\),如何選取物品,是的揹包中裝入的物品的總價值最大?函數

    在約束條件爲:選取物品的重量小於等於揹包重量的狀況下,儘量讓揹包中物品的總價值最大。測試

    根據問題描述,設計以下的約束條件和目標函數:優化

約束條件:spa

\[ \begin{equation} \left\{ \begin{array}{**lr**} \sum_{i=1}^{n} w_ix_i \leq W \\ x_i \in \{0,1\}, 1 \leq i \leq n & \end{array} \right. \end{equation} \]設計

目標函數:code

\[ max\sum_{i=1}^{n} v_ix_i \]

    問題如今等價於,尋找一個在知足約束條件狀況下,並使目標函數達到最大的解 \(X=(x_1,x_2,...,x_n)\)

步驟2

    算法設計,包括策略與數據結構的選擇

    設計用二維數組對物品信息進行記錄: \(C[i][j]\) 用來記錄若是當前還有\(i\)個物品,揹包容量還剩\(j\)的狀況下,當前揹包所能獲得的最大價值。

很容易發現條件即:\[C[0][j] = C[i][0] = 0\]

遞歸定義應該爲:

\[ \\ C[i][j]= \begin{equation} \left\{         \begin{array}{**lr**}         C[i-1][j], & j < w_i \\ max\{C[i-1][j],C[i-1][j-w_i]+v_i\}, & j \ge w_i \end{array}   \right.   \end{equation} \]

    能夠這樣理解,每一個物品我能夠選擇是否加入到揹包中,首先判斷,當前物品是否重量已經大於揹包所能容納的重量;若是能容納該物體,則進行判斷加入該物品\(C[i-1][j-w_i]+v_i\)獲得的價值更高,仍是不加入該物品\(C[i-i][j]\)所能獲得的物品的總價值更高。

步驟3

    描述算法。但願採用源代碼之外的形式,如僞代碼或流程圖等;

僞代碼表示:

01PACKAGE(n,w,v,W) // n爲物品的個數,w爲重量, v爲價值,W爲揹包容量
    // C[1..n,1..n]爲最優解
    for i=1 to n:
        do C[i][0] = 0
    for j=1 to W:
        do C[0][j] = 0
    for i=1 to n:
        for j=1 to W:
            do
            if j < w[i]:
            then C[i][j]=C[i-1][j]
            else
            then C[i][j]=max{C[i-1][j],C[i-1][j-w[i]]+v[i]}
    return C

步驟4

    算法的正確性證實。須要這個環節,在理解的基礎上對算法的正確性給予證實;

對該算法進行最優子結構的證實:

假設\(X=(x_1,x_2,x_3,...,x_n)\)是揹包問題的最優解,那麼\((x_2,..,x_n)\)是下面問題的一個最優解:

\[ \begin{equation} \left\{ \begin{array}{**lr**} \sum_{i=2}^{n} w_ix_i \leq W-w_1x_1 \\ x_i \in \{0,1\}, 2 \leq i \leq n & \end{array} \right. \end{equation} \]

目標函數: \[max\sum_{i=2}^nv_ix_i\]

即除去第一個物品之後的子問題

證實以下:(反證法)

\(X=(x_2,...,x_n)\)不是上述子問題的最優解,設\(Y=(y_2,...,y_n)\)是上述問題的最優解,則\(Y\)所求的目標函數的值必定比X求得的目標函數的值更大,即:
\[ \sum_{i=2}^nv_iy_i > \sum_{i=2}^nv_ix_i \]
\(Y\)知足約束條件:\(\sum_{i=2}^nw_iy_i \leq W-w_1x_1\),即 \(w_1x_1+\sum_{i=2}^nw_iy_i \leq W\),該不等式證實\((x_1,y_1,y_2,...,y_n)\)是原問題的一個解。

在公式 \((5)\) 中左右同時加上\(v_1x_1\),可得:\[ v_1x_1+\sum_{i=2}^nv_iy_i > v_1x_1+\sum_{i=2}^nv_ix_i\],說明\((x_1,y_1,y_2,...,y_n)\)要比\((x_1,x_2,...,x_n)\) 方案價值更高,因此\((x_1,x_2,...,x_n)\)不是最優解,產生了矛盾。

因此其最優子結構的性質得證。

步驟5

    算法複雜性分析,包括時間複雜性和空間複雜性;

  • 求解0-1揹包問題部分

時間複雜性分析:

因爲只須要遍歷進行,因此只須要考慮循環中的複雜度便可。

\[ T(n) =O(n)+O(W)+O(nW) \\ =O(nW) \]

空間複雜度,主要是生成數組時佔用的空間。

\[ T(n)=O(n^2) \]

  • 獲得最優解部分

時間複雜度分析:

只須要一個循環便可。

\[ T(n)=O(n) \]
空間複雜度爲:\[O(1)\]

步驟6

    算法實現與測試。附上代碼或以附件的形式提交,同時貼上算法運行結果截圖;

# -*- coding: utf-8 -*-
"""
Created on Fri Sep 28 12:44:40 2018
@theme: 算法準備-01揹包問題
@author: pprp
"""

import numpy as np

def solvePackage(n,w,v,W):
    """solve the 01 package problem"""

    C=np.zeros((n+1,W+1))
    
    for i in range(n):
        C[i][0]=0
    for i in range(W):
        C[0][i]=0

    for i in range(1,n+1):
        for j in range(1,W+1):
            if j < w[i-1]:
                C[i][j]=C[i-1][j]
            else:
                C[i][j]=max(C[i-1][j],C[i-1][j-w[i-1]]+v[i-1])
    return C
        
def getSolution(n,w,W,C):
    j = W
    x = np.zeros(n)
    for i in range(n,0,-1):
        if C[i][j]==C[i-1][j]:
            x[i-1] = 0
        else:
            x[i-1] = 1
            j -= w[i-1]
    return x
if __name__ == "__main__":
    n = 11
    w = np.array([2, 6, 3, 4, 2, 8, 2, 4, 7, 5, 1])
    v = np.array([10,23,5,34,23,17,22,32,12,15,32])
    W = 15

    C=solvePackage(n,w,v,W)
    x=getSolution(n,w,W,C)

    print("packages:",x)

    print("Output:\n",C)

實驗結果

packages: [1. 0. 0. 1. 1. 0. 1. 1. 0. 0. 1.]

驗證結果:10+34+23+22+32+32=153

實驗總結

動態規劃基本思想

    動態規劃算法一般是用來解決某種最優性質的問題。基本思想是將帶求解問題劃分爲若干個子問題,先求解子問題,而後從子問題的解獲得原問題的解。動態規劃與分治法的區別在於,動態規劃的子問題多是互相重疊的重複計算的,分治法則是相互獨立的。能夠用一個表來記錄子問題是否已經求解,這樣能夠避免重複求解。

動態規劃應用條件

    須要知足最優化原理、無後效性和重疊性。

  • 最優化原理,一個最優化策略的子策略必定是最優的,就是知足最優子結構的性質。

  • 無後效性,一個階段之前各階段的狀態沒法直接硬性它將來的決策,只能經過當前的這個狀態。
  • 重疊性,就是記錄已經解決過的問題,須要存儲已經解決過的問題,空間複雜度比較大,是一種以空間換時間的算法。

難點

    動態規劃的難點在於,如何根據問題的最優子結構的性質,構造動態規劃方法中的遞歸公式或動態規劃方程。就好比本問題中,如何設計這個方程纔是難點所在。

遇到的問題

    在逆向求解使用的哪幾個揹包的時候,因爲對問題理解的不深入,致使彙總的時候發現計算結果出現了問題,也就是\(getSolution\)這個函數出現了問題。應該逆向進行求解問題,也就是從後往前進行推導,更改了循環的方向之後就能夠獲得最終的結果了。

心得

    只有在真正理解算法的基礎上,而後加以僞代碼的梳理,這時候寫才能一鼓作氣。另外還須要對代碼計算出來的結果進行人工覈查,防止某些問題被忽略掉。另外這個問題已經被老師分析的比較透徹,因此寫起來沒有太大的困難。可是碰見新的問題的時候,如何構造解決問題的方法纔是難點所在。

相關文章
相關標籤/搜索