孫佳寧,馬海龍,張立臣,李 鵬*
(1.現代教學技術教育部重點實驗室,陜西 西安 710062;2.陜西省教學信息技術工程實驗室,陜西 西安 710119;3.陜西師范大學 計算機科學學院,陜西 西安 710119)
背包問題是指,在指定的背包容量下,如何選擇總價值最大的物品裝入背包。在背包問題中,若每個物品只能被選擇一次,且裝入時不可拆分,稱為0-1背包問題。0-1背包問題是組合優化問題中經典的NP完全問題,一直以來得到廣泛的研究和應用,如貨物裝載、投資選擇、密鑰生成等。
隨著科技的發展和新型算法策略的不斷涌現,0-1背包問題的求解算法也由經典的蠻力法、貪心算法、動態規劃法、回溯法、分支限界法等逐漸發展出眾多新型智能算法,如螢火蟲算法、量子狼群算法、煙花算法、混合蝙蝠算法等。這些新型算法在一定程度上能夠提高搜索能力和求解速度,但并不能保證找到問題的最優解,其算法的高效性是以犧牲算法的最優性為代價的。
在保證得到0-1背包問題最優解的前提下,該文以貪心算法和回溯算法為基準,以提高算法性能為目標,對其進行改進,提出了一種新型回溯算法。該算法利用了“回溯算法在搜索過程中判斷、調試”的特點,并通過先運行得到的貪心算法的近似解用于經典回溯算法剪枝策略的判斷條件,同時優化了該算法中針對物品選擇的判斷條件。
通過大量的仿真實驗,如設置每個物品的重量和價值、背包的容量、背包可容納的物品數量的最大上限等,設計對比實驗,觀察和分析了經典的回溯算法與該文提出的新型回溯算法找到問題最優解時的時間耗費,驗證所提出算法的高效性和可靠性。
經典的0-1背包問題指的是給定一些物品和一個背包,每個物品的重量和價值已知,背包的容量已知。選擇一定的物品裝入背包,在選擇的過程中,要求每個物品只能被選擇一次,且每次只有裝入或不裝入兩種選項(即物品不可分割)。如何進行物品的選擇,才能使放入背包的物品總重量在不超過背包容量的前提下,擁有最大的價值總和。
假設物品數量為n
,背包容量為C
,第i
個物品的重量為w
[i
],價值為v
[i
],則0-1背包問題的形式化模型表示如下:
x
[i
]表示物品i
的選擇狀態,x
[i
]=1表示選中第i
個物品放入背包,x
[i
]=0表示第i
個物品沒有放入背包。眾所周知,貪心算法兼顧了問題求解的可行性和算法執行的高效性,是近似解決最優化問題較簡單、較迅速的求解算法。在問題求解的過程中,貪心算法并不考慮問題的整體性,只考慮當前條件下的最優選擇,通過局部最優解逐步構造出問題的解,因而往往只能得到問題的近似最優解。
常見的貪心策略有“物品重量最小優先”、“物品價值最大優先”和“物品單位價值最大優先”。在實際應用中,“物品單位價值最大優先”的貪心策略往往效果最好,因而該文采用該貪心策略,其主要步驟描述如下:
(1)計算每個物品的單位重量價值,并按照該單位價值以遞減順序對物品進行排序,排序后的結果存儲到一維數組index中;
(2)根據排序后的結果,按照單位價值從大到小的順序,依次將每個物品放入背包,直到背包內放不下新的物品為止;
(3)物品最終的選擇狀態存儲到一維數組result中,背包內物品的最大價值存儲到maxvalue中。
上述“物品單位價值最大優先”的貪心算法(greedy)的偽代碼描述如下:
算法1:貪心算法greedy。
1.maxvalue←0
2.fori
=1 ton
do3.index[i
]←i
;4.sort[i
]←v
[i
]÷w
[i
];5.fori
=1 ton
do6.forj
=1 ton
-i
do7.if sort[j
]j
]?sort[j
+1];9.index[j
]?index[j
+1];10.fori
=1 ton
do11.if w[index[i
]]≤C then12.x
[index[i
]]←1;13.C
←C
-w[index[i
]];14.else
15.break;
16.fori
=1 ton
do17.maxvalue←maxvalue+x
[i
]*v
[i
];18.result[i
]←x
[i
];19.return maxvalue
算法1的時間復雜度主要由排序算法決定。由于經典冒泡排序算法的時間復雜度為O
(n
),貪心選擇(代碼10到15行)的時間復雜度為O
(n
),故算法1的時間復雜度為O
(n
),其中n
為物品的數量。若將排序算法改為快速排序算法,可以將該算法的時間復雜度降低為O
(n
logn
)。與貪心算法相比,經典回溯算法更看重問題的整體性分析。經典回溯算法是使用深度優先遍歷求解0-1背包問題的經典算法。在問題求解的過程中,回溯算法將物品的選擇狀態構造成一個解空間樹,通過遍歷解空間樹中的每個節點來尋找問題的可行解和最優解。具體地說,經典的回溯算法從解空間樹的根節點出發,依次判斷解空間樹的每一個節點,并選擇當前狀態下滿足問題約束條件的節點,當遍歷到葉子節點時,算法對當前各個節點的選擇狀態進行記錄和判斷,當約束條件不滿足時,回退一步至上一狀態或回退多步,直至遍歷過解空間樹的每一個節點并回溯到根。在搜索的過程中,回溯算法會不斷判斷是否存在更優的解,并記錄和更新目前已找到的最優解。
經典回溯算法解決0-1背包問題的主要步驟如下:
(1)構造問題的解空間樹;
(2)確定問題的約束條件。一個是放入背包的物品總重量不超過背包的容量,另一個是放入背包的物品是否構成問題的最優解;
(3)從根節點開始,深度優先遍歷解空間樹,在遍歷的過程中根據問題的約束條件選擇性地進行回溯和繼續遍歷。
在使用經典回溯算法解決0-1背包問題時,最壞情況下需要遍歷整個解空間樹,此時算法的時間復雜度為O
(2)。但一般情況下,回溯算法總會在遍歷到解空間樹的最后一個節點前找到問題的解,在實際的遍歷過程中,算法的運行時間取決于遍歷時生成的節點數目,即在找到問題的最優解時,算法所遍歷到的節點數目。1.3.1 遞歸回溯算法
存在兩種實現經典回溯的算法,一個是遞歸算法,一個是非遞歸算法。遞歸回溯算法,是指把一個大型的復雜問題分解為一個與原問題相似的、規模較小的問題,通過遞歸求解小問題并將子問題的解合并,從而得到原問題的解。
算法2給出了遞歸回溯算法(knap1)的偽代碼描述,該算法從位于解空間樹根節點的第1個物品開始遍歷,依次考慮當前選中物品不放入背包和放入背包時,所有物品的選擇狀態。當遍歷到葉子節點時,進行最優解的約束條件檢驗并判斷是否進行回溯,算法的主要步驟如下:
(1)引入cw和cv分別表示當前狀態下背包內物品的總重量和總價值;
(2)對于每一個物品,首先選擇“不裝入背包”并進行遞歸,而在進行“裝入背包”的選擇前,則需要判斷所有選中物品的總重量是否滿足背包的容量;
(3)當遍歷到最后一個物品時,計算此時背包內物品的總價值,判斷是否為問題的最優解。
算法2:遞歸回溯算法knap1。
Initialize maxvalue, cw, cv as 0
knap1(1)
output maxvalue
intknap1(int i)
if i=n+1 then
if cv>maxvalue then
maxvalue←cv;
result←x;
else
x[i]←0;
knap1(i+1);
if cw+w[i]≤C then
x[i]←1;
cv←cv+v[i], cw←cw+w[i];
knap1(i+1);
x[i]←0;
cv←cv-v[i], cw←cw-w[i];
returnmaxvalue
1.3.2 非遞歸回溯算法
與遞歸回溯算法不同,非遞歸回溯算法利用物品的狀態值判斷是否需要回溯,而不需要借助操作系統提供的遞歸機制,因而一般具有較高的時空效率。
算法3給出了非遞歸回溯算法(knap2)的偽代碼描述,在該算法中,選中物品在進行“裝入”或“不裝入”背包的選擇前,其狀態值首先會被賦值為-1(代碼第2行);當物品的選擇狀態確定后,其狀態值則為0或1(代碼第4、15行);一旦該物品的狀態值增加至2時,則需要進行回溯(代碼第17、18行)。
算法3:非遞歸回溯算法knap2。
1.Initialize maxvalue as 0,i
as 12.x
[i
]←-13.whilei
≥1 do4.x
[i
]++;5.ifx
[i
]≤1 then6.ifi
=n
then7.forj
=1 ton
do8.cw←cw+x
[j
]*w
[j
];9.cv←cv+x
[j
]*v
[j
];10.if cw≤C and cv>maxvalue then
11.maxvalue←cv;
12.result←x;
13.cw←0, cv←0;
14.else
15.i
++;16.x
[i
]←-1;17.else
18.i
--;19.end
20.return maxvalue
使用貪心算法求解0-1背包問題時,算法從問題的某一初始解出發,選擇當前條件下的最優解,并試圖構造或逼近問題的整體最優解。貪心算法每一步的決策僅考慮當前的局部信息,其解空間不可回溯再現,具有較強的隨機性和不可預知性。這種基于經驗或直覺的判斷,并不一定能夠保證找到問題真正的最優解,在絕大多數情況下,貪心算法得到的解只是問題的近似最優解。但是,由于貪心算法采用局部最優決策,因而往往具有較高的效率。
回溯算法的思想和枚舉法類似,即通過嘗試問題所有可能的解來尋找問題的最優解,這種算法雖然確保了解的正確性,但是以犧牲算法運行時間為代價。當問題規模較大時,回溯算法的運行時間呈指數式增長,在短時間內可能無法得到問題的最優解。
針對上述特征,該文將貪心算法的高效性和回溯算法的最優性相結合,提出了一種融合貪心策略和剪枝策略的新型回溯算法,簡稱新型算法。考慮到貪心算法得到的解雖然可能不是問題的最優解,但至少一定是問題的近似解,故將其作為初始解應用于回溯算法,從而可以盡快實現剪枝操作,進而提高回溯算法效率。
因此,新型算法將貪心算法的解作為回溯算法中剪枝策略的判斷條件,同時優化了遍歷過程中的約束條件,從而在確保得到問題最優解的同時,可以進一步提高算法效率。
算法4給出了所提出的新型算法的偽代碼描述。該算法使用遞歸方法求解0-1背包問題,與經典的回溯算法不同,首先,算法4增加了基于價值的剪枝策略,若背包內物品的總價值與未遍歷到的物品的總價值之和小于算法1得到的解,則進行剪枝并回溯。其次,在遍歷的過程中,算法4優化了物品選擇的約束條件,將經典回溯算法的約束條件“放入背包的物品總重量不超過背包的容量”修改為“當前選中的物品重量滿足背包的剩余容量”。
所提出新型算法的主要步驟如下:
(1)引入pw和pv分別表示前i
-1個物品中,已經放入背包的物品的總重量和總價值,rw表示背包的剩余容量,rv表示未遍歷到的物品的總價值(包含當前物品);(2)算法1得到的解存儲到maxvalue_greedy中;
(3)定義遞歸函數GB(i
, rw, pw, rv, pv),從初始狀態GB(1,C
, 0, totalvalue, 0)開始遞歸;(4)首先判斷選中物品“不裝入背包”時是否需要剪枝;
(5)若選中物品“裝入背包”,首先判斷物品重量是否滿足背包的剩余容量,若“能裝入”,則繼續進行步驟4的剪枝判斷,最終確定物品的選擇狀態。
算法4:新型算法。
1.Initialize maxvalue, pw, pv as 0, rw asC
, rv as totalvalue,i
as 12.maxvalue_greedy←greedy()
3.GB(1,C
, 0, totalvalue, 0)4.output maxvalue
int GB(inti
, int rw, int pw, int rv, int pv)1.ifi
=n
+1 then2.fori
ton
do3.value←cv+v
[i
]*x
[i
];4.if value>maxvalue then
5.maxvalue←value;
6.result←x;
7.else
8.if pv+rv-v
[i
]>maxvalue_greedy then9.GB(i
+1, rw, pw, rv-v
[i
], pv);10.ifw
[i
]≤rw then11.if pv+rv>maxvalue_greedy then
12.x
[i
]←1;13.GB(i
+1, rw-w[i
], pw+w
[i
], rv-v
[i
], pv+v
[i
]);14.x
[i
]←0;15.return maxvalue
在新型算法中,遞歸函數GB(第3行)的含義是:若當前考慮的物品“不裝入背包”,計算此時背包內物品的總價值與未遍歷到的物品的總價值之和,若大于maxvalue_greedy,則表明,在當前物品的選擇狀態下,存在將某些物品裝入背包后使得總價值提高的可能,因此進行遞歸;否則,表明在當前物品的選擇狀態下,任何后續的裝入選擇都將不可能超過現在背包內物品的總價值,此時則進行剪枝并回溯。若當前選中物品想要“裝入背包”,首先判斷該物品的重量是否滿足背包的剩余容量,然后繼續判斷是否“有必要裝入”。當遍歷到最后一個物品時,計算此時背包內物品的總價值并判斷是否為問題的最優解。
新型算法的核心思想是:對于每一個物品,只有該物品“能裝入背包”且“有必要裝入”,才會將其放入背包中。
該文進行了大量的仿真實驗,具體實驗環境為:CPU:i7-8550U,內存:DDR2 16 GB;硬盤:1 TB;緩存,8 MB;PF使用率:36%~45%;編程語言:Java,軟件開發工具:Eclipse。分別實現了遞歸回溯算法(算法2)、非遞歸回溯算法(算法3)和新型算法(算法4),并在不同問題規模下進行仿真實驗與分析。
實驗參數設置如下:根據物品數量n
,算法按照均勻分布隨機地產生1~20之間的整數作為每個物品的重量和價值,背包容量為物品總重量與一個預先定義的實驗系數f
之積,其中,f
為一個0到1之間的小數,f
越小,表示背包的容量越小。f
為0.3、0.6、0.8,在不同問題規模n
下,使用遞歸回溯算法(算法2)、非遞歸回溯算法(算法3)和新型算法(算法4)得到問題最優解時的時間耗費進行實驗。實驗結果依次見表1~表3。
表1 三種算法在實驗系數f=0.3的時間耗費

表2 三種算法在實驗系數f=0.6的時間耗費

表3 三種算法在實驗系數f=0.8的時間耗費
通過上述比較,可以得出以下結論:
(1)在不同實驗系數下,隨著問題規模的增加,所有算法的時間耗費都呈現上升趨勢,其中經典的遞歸回溯算法和非遞歸回溯算法的上升趨勢更為顯著。結合算法的時間復雜度可知,經典回溯算法的時間耗費隨著問題規模的增加呈指數級增長。此外發現,非遞歸回溯算法較遞歸回溯算法具有較高的運行時間。
(2)在同一實驗系數、同一問題規模下,與經典回溯算法相比,該文所提出的新型算法在保證得到問題的最優解的同時,具有較小的時間耗費,特別是在問題規模較大時,效果更為明顯。
在不同實驗系數下,對遞歸回溯算法(算法2)和非遞歸回溯算法(算法3)的時間耗費進行了對比,從而驗證實驗系數是否對經典回溯算法的執行時間產生影響。實驗結果見表4。

表4 經典回溯算法在問題規模n=20時的時間耗費
根據表4可知,當實驗系數從0.3逐漸增加至0.9時,遞歸回溯算法的時間耗費線性增長至某一數值后,在該數值處呈現小幅度波動;非遞歸回溯算法的時間耗費則始終在某數值處小幅度波動。
從表中可知,當實驗系數增大至0.7時,兩種回溯算法的時間耗費都基本保持穩定,故取實驗系數f
=0.8,單獨對新型算法進行實驗,分析其算法運行時間與問題規模間的關系。實驗結果如圖1所示。
圖1 新型算法的時間耗費(f=0.8)
從圖1可以看出,在實驗系數為0.8時,只有當問題規模近似增加至70時,新型算法的時間耗費才會有較為明顯的遞增,效率遠遠高于經典回溯算法。
表5列出了在不同的實驗系數和不同的問題規模下,新型算法得到問題最優解的時間耗費情況。從表5中可知,新型算法普遍隨實驗系數的增加而減少,其原因在于,隨著實驗系數的增加,背包的容量不斷增大,背包可以容納的物品數量相應增加,從而減少了回溯的概率,進而提高了算法效率。

表5 新型算法在不同實驗系數、不同問題規模下的時間耗費
0-1背包問題是經典的NP完全問題,而經典的回溯算法采用枚舉的思想,通過深度優先搜索解空間樹尋找問題的最優解。在問題規模較小時,回溯算法同其他求解0-1背包問題的經典算法相比,具有更小的時間耗費和空間耗費。但隨著問題規模的增加,回溯算法在時間和空間上都有極為明顯的增加,這種增加導致算法在較短時間內無法得到問題的最優解。
該文將貪心算法的高效性和經典回溯算法的最優性相結合,提出的融合貪心策略與剪枝策略的新型算法,將經典貪心算法得到的問題近似解用于剪枝策略的判斷條件中,減少了經典回溯算法遍歷過程中的無效搜索;同時,在進行物品選擇的判斷時,使用背包剩余容量作為約束條件,大大提高了算法的求解效率。大量仿真實驗結果表明,該新型算法在保證得到問題最優解的同時,有效提高了算法的運行效率。