動態規劃法-01揹包問題

一 幾個概念:

最優化問題:有n個輸入,它的解由這n個輸入的一個子集組成,這個子集必須滿足某些事先給定的條件,這些條件稱爲約束條件,滿足約束條件的解稱爲問題的可行解。滿足約束條件的可行解可能不止一個,爲了衡量這些可行解的優劣,事先給出一定的標準,這些標準通常以函數的形式給出,這些標準函數稱爲目標函數,使目標函數取得極值的可行解成爲最優解,這類問題稱爲最優化問題。


二 最優性原理:

對於一個具有n個輸入的最優化問題,其求解的過程往往可以劃分爲若干個階段,每一個階段的決策僅依賴前一階段的狀態,由決策所採取的動作使狀態發生轉移,成爲下一個階段的依據。從而,一個決策序列在不斷變化的狀態中產生。這個決策序列產生的過程稱爲多階段決策過程。

在每一個階段的決策中有一個賴以決策的策略或目標,這種策略或目標是由問題的性質和特點所確定,通常以函數的形式表示並具有遞推關係,稱爲動態規劃函數。

多階段決策過程滿足最優性原理:無論決策過程的初始狀態和初始決策是什麼,其餘決策必須相對於初始決策所產生的當前狀態,構成一個最優決策序列。

如果一個問題滿足最優性原理通常稱此問題具有最優子結構性質

三 特徵

我們知道動態規劃是求解全局最優解的有效方法,一般來說能用動態規劃算法求解的問題具有以下兩個顯著特稱:

  • 具有最優子結構性質,也就是說可以遞歸的定義最優解。

  • 能夠分解爲相互重疊的若干自問題。

  • 最優解中也包含其子問題的最優解。


四 設計方案

動態規劃法設計方案

  • 分段:將原問題分解爲若干個相互重疊的子問題;

  • 分析:分析問題是否滿足最優子結構性質,找出動態規劃函數遞推式;

  • 求解:利用遞推式自底向上計算,實現動態規劃過程。


五 01揹包問題

0/1揹包問題中,物品i或者被放入揹包,或者不被放入揹包,設Xi表示物品i裝入揹包的情況,則當Xi=0時,表示物品i沒有被裝入揹包,Xi=1時,表示物品i被裝入揹包。根據問題的要求,有如下約束條件和目標函數:

圖 1 約束條件


圖2 目標函數

01揹包的狀態轉換方程f[i,j]= Max{ f[i-1,j-Wi]+Pi( j >= Wi ),  f[i-1,j] }

f[i,j]表示在前i件物品中選擇若干件放在承重爲j的揹包中,可以取得的最大價值。

Pi表示第i件物品的價值。

決策:爲了揹包中物品總價值最大化,第i件物品應該放入揹包中嗎?

六 動態規劃法解決方案

有編號分別爲a,b,c,d,e的五件物品,它們的重量分別是2,2,6,5,4,它們的價值分別是6,3,5,4,6,現在給你個承重爲10的揹包,如何讓揹包裏裝入的物品具有最大的價值總和

name

weight

value

1

2

3

4

5

6

7

8

9

10

a

2

6

0

6

6

9

9

12

12

15

15

15

b

2

3

0

3

3

6

6

9

9

9

10

11

c

6

5

0

0

0

6

6

6

6

6

10

11

d

5

4

0

0

0

6

6

6

6

6

10

10

e

4

6

0

0

0

6

6

6

6

6

6

6

10/1揹包問題動態規劃法

1採用自底向上自左向右的方式生成的。

e1表示在揹包容量爲1的情況下僅僅放入物品e的價值,因容量1<4因此價值爲0;依此類推當揹包容量爲4時,單元格e4的價值爲6。容量繼續上漲也只能放置e,因此價值持續爲6

d行可參照遞推式f[i,j]= Max{ f[i-1,j-Wi]+Pi( j >= Wi ), f[i-1,j] }如下描述

  1. i<=5時,d行表格價值與e行相同;

  2. i>=6時,比如第6行,選取Max{f[1,1]+4,f[1,6]}Max{e1+4, e6}=Max{4, 6} = 6;依此類推d9=Max{f[1,4]+4,f[1,59}=10;

依次類推可計算c/b/a的值,然後根據最大價值返算最優路徑。


d1表示揹包容量爲1的情況下放置物品de後的價值,因e的重量爲4>1,且d的重量爲5>1因此價值爲0。直至第5(不包括d5)d行表格的內容與e行相同,



七 程序實現C#

代碼段1 測試函數

class MainClass
	{
		public static void Main (string[] args)
		{
            Obj[] objs = new Obj[4];
            objs[0] = new Obj() { Weight = 7, Price = 42 };
            objs[1] = new Obj() { Weight = 3, Price = 12 };
            objs[2] = new Obj() { Weight = 4, Price = 40 };
            objs[3] = new Obj() { Weight = 5, Price = 25 };


            			Console.WriteLine ("Dynamicprogramming Model:");            
			dynamicprogramming d = new dynamicprogramming (objs, 10);
			int price = d.Execute ();
			d.Printing (price);
            //Console.Read();
		}
	}

代碼段2 實現函數


public class dynamicprogramming
	{
		/// <summary>
		/// 存儲動態規劃的遞推數據
		/// m_V[i][j]表示前i個物品放入容量爲j的揹包獲得的最大價值
		/// 其中第0行作爲初始化,表示不放入物品時的價值;
		/// 第0列作爲初始化,表示容量爲0時能放入物品的價值;
		/// </summary>
		private int[,] m_V;
		/// <summary>
		/// 物品數量
		/// </summary>
		private int m_n;
		/// <summary>
		/// 揹包容量
		/// </summary>
		private int m_w;

		public Obj[] objs {
			get;
			set;
		}
		public dynamicprogramming (Obj[] objs, int w)
		{
			if (objs == null) {
				return;
			}

			m_n = objs.Length;
			this.objs = objs;
			m_w = w;
			m_V = new int[m_n + 1, m_w + 1];

			for (int i = 0; i < m_w+1; i++) {
				m_V [0, i] = 0;
			}
			for (int i = 0; i < m_n+1; i++) {
				m_V [i, 0] = 0;
			}
		}

		public int Execute()
		{
			for (int r = 1; r <= m_n; r++) {
				for (int w = 1; w <= m_w; w++) {
					if (w < objs[r - 1].Weight) {
						m_V[r, w] = m_V[r - 1, w];
					} else {
						int tmp = m_V [r - 1, w - objs [r - 1].Weight] + objs [r - 1].Price;
						m_V [r, w] = m_V [r - 1, w] > tmp ? m_V [r - 1, w] : tmp;
					}
				}
			}

			//求裝入揹包的物品
			int weight = m_w;
			for (int i = m_n; i > 0; i--) {
				if (m_V[i, weight] > m_V[i - 1, weight]) {
					objs [i - 1].Selected = true;

					weight -= objs [i - 1].Weight;
				}
			}

			return m_V[m_n, m_w];
		}

		public void Printing(int price)
		{
			Console.Write("<");
			for (int i = 0; i < objs.Length; i++)
			{
				Console.Write(objs[i].Selected ? "1 " : "0 ");

			}
			Console.WriteLine(">, price: " + price );
		}
	}

輸出結果:

注:上述程序使用FreeBSD下Mono開發。


發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章