回溯法(深度优先)剪枝和分支限界法(宽度优先)剪枝对比:01背包问题
生活随笔
收集整理的這篇文章主要介紹了
回溯法(深度优先)剪枝和分支限界法(宽度优先)剪枝对比:01背包问题
小編覺得挺不錯的,現(xiàn)在分享給大家,幫大家做個參考.
限界函數(shù):
CurValue + rest <= BestValue
回溯法(深度優(yōu)先)剪枝
# 遞歸方式 class pack_01_back_prune_test: def __init__(self,N,V,C,W):self.num =Nself.V = Vself.C = Cself.W = Wself.BestResult = [False]*Nself.Selected = [False]*Nself.BestValue = 0self.CurCost = 0self.CurValue = 0# bound()限界函數(shù)self.rest = 0for i in range(N):self.rest += W[i]def pack_01_back_tracking(self,depth):if depth > self.num-1:if self.CurValue > self.BestValue:self.BestValue = self.CurValue self.BestResult[:] = self.Selected[:]else:# 滿足約束條件和限界函數(shù)的處理if self.CurCost + self.C[depth] <= self.V and self.CurValue + self.rest > self.BestValue:self.Selected[depth] = Trueself.CurCost += self.C[depth]self.CurValue += self.W[depth]self.rest -= self.W[depth]# nextself.pack_01_back_tracking(depth+1)# undoself.CurCost -= self.C[depth]self.CurValue -= self.W[depth]self.rest += self.W[depth]# 滿足限界函數(shù) if self.CurValue + self.rest > self.BestValue:self.Selected[depth] = Falseself.pack_01_back_tracking(depth+1)def print_Result(self):self.pack_01_back_tracking(0)print(self.BestResult)print(self.BestValue) # 迭代方式 #%% # 這種解法注意回溯函數(shù)一致性,思路比較清晰:滿足剪枝條件就回溯:CurValue + rest <= BestValue # 這是基于深度優(yōu)先搜索的回溯法剪枝,以下是迭代實現(xiàn)方法 def pack_01_back_prune_iteration_test(N,V,C,W):depth = 0BestResult = [False]*NSelected = [False]*(N)BestValue = 0CurCost = 0CurValue = 0 # bound()限界函數(shù)rest = 0for i in range(N):rest += W[i]while True:# 盡量向左走直到不滿足約束條件while depth < N and CurCost + C[depth] <= V:rest -=W[depth]Selected[depth] = TrueCurCost += C[depth]CurValue += W[depth]depth +=1# 走到底,結(jié)果處理if depth >= N:BestValue = CurValue BestResult[:] = Selected[:]# 不能往左走,就向右走,注意這里只是走一步而已else:rest -=W[depth]Selected[depth] =Falsedepth +=1# 當不滿足限界函數(shù)的時候,就需要回溯,注意底部也滿足這個條件while CurValue + rest <= BestValue:# 回溯的處理,之所有需要depth -=1,上面走的時候都depth++了,底部也是這樣depth -=1while depth >=0 and not Selected[depth]:rest +=W[depth]depth -=1# 當回溯到root的之后,無法回溯了,輸出結(jié)果if depth < 0:return BestResult,BestValue# 回溯恢復現(xiàn)場else:Selected[depth] =FalseCurCost -= C[depth]CurValue -= W[depth]depth +=1 # 運行 N = 8 V = 30 C = [11,2,3,9,13,6,15,7,19] W = [5.0,2.0,5.0,7.0,5.0,11.0,6.0,14.0]print(pack_01_back_prune_iteration_test(N,V,C,W))pack_01_back_prune_test(N,V,C,W).print_Result() ([False, True, True, True, False, True, False, True], 39.0) [False, True, True, True, False, True, False, True] 39.0分支限界法(寬度優(yōu)先)剪枝
也使用一樣的限界函數(shù):
CurValue + rest <= BestValue
回溯法深度優(yōu)先很快就能得到一個可行解,進而可以更新BestValue,分支限界法寬度優(yōu)先,可行解都在最后一層,BestValue一直得不到更新,那就是限界函數(shù)一直不起作用
為了使限界函數(shù)早生效,我們應該提前更新BestValue,在滿足約束條件下,進入左子樹就可以更新BestValue,因為最終的BestValue是滿足約束條件下Curvalue里面的最大值
代碼實現(xiàn):
#%% class FIFO_01_Pack_prune:def __init__(self,N,V,C,W):self.num =Nself.Volume = Vself.Cost = Cself.Value = Wself.BestValue = 0#用于存放活結(jié)點,便于理解,把根結(jié)點,以及第0層結(jié)束標志-1放進去# 結(jié)點包括2個屬性:當前空間大小,當前的價值大小self.queue = [[0,0],[-1,-1],] # 當前剩余價值和,bound()限界函數(shù)self.rest = 0# 把第一個減去,因為我們要在進入這一層前更新restfor i in range(1,N):self.rest += W[i]# 實現(xiàn)時葉子結(jié)點不加入到活結(jié)點列表def enQueen(self,pair,depth):if depth < self.num -1:self.queue.append(pair)def pack_01(self): # selected = [0]*self.num # 首先取出根結(jié)點depth = 0pair = self.queue.pop(0)CurCost = pair[0]CurValue = pair[1]while True:# 判斷左結(jié)點能否加入到隊列,能的話,把當前空間和當前價值放入隊列,滿足約束條件if CurCost + self.Cost[depth] < self.Volume:# 滿足限界函數(shù)if CurValue + self.Value[depth] + self.rest > self.BestValue:# 在進入左子樹時,更新bestvalueself.BestValue = CurValue + self.Value[depth] self.enQueen([CurCost + self.Cost[depth],CurValue + self.Value[depth]],depth)# 右滿足限界函數(shù)if CurValue + self.Value[depth] + self.rest > self.BestValue:self.enQueen([CurCost,CurValue],depth)# 然后彈出下一個結(jié)點pair = self.queue.pop(0)CurCost = pair[0]CurValue = pair[1]# 當同一層處理完畢時,先判斷是否能夠輸出結(jié)果,判斷的標準是隊列是否為空,# 這時下一層的所有結(jié)點已經(jīng)加入了隊列,這時需要把下一層# 增加一個結(jié)尾-1便于判斷,然后進入下一層,彈出下一個結(jié)點if CurCost == -1:if not self.queue:return self.BestValueself.enQueen([-1,-1],depth)depth += 1# 在剛進入下一層時,更新restself.rest -= self.Value[depth]# 彈出下一個結(jié)點pair = self.queue.pop(0)CurCost = pair[0]CurValue = pair[1]def print_Result(self):print(self.pack_01()) #%% N = 8 V = 30 C = [11,2,3,9,13,6,15,7,19] W = [5.0,2.0,5.0,7.0,5.0,11.0,6.0,14.0]#pack_01_back_test(N,V,C,W).print_Result() FIFO_01_Pack_prune(N,V,C,W).print_Result()不知道大家注意到?jīng)]有?上述實現(xiàn)方式?jīng)]有使用單位體積價值的排序,和之前提到01背包回溯法基于單位體積價值實現(xiàn)不一樣(先裝單位體積價值高的)。
總結(jié)
以上是生活随笔為你收集整理的回溯法(深度优先)剪枝和分支限界法(宽度优先)剪枝对比:01背包问题的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 限界分支法(实际上没有剪枝,介绍的是广度
- 下一篇: 限界分支法(队列方式)追踪解:01背包问