1. 程式人生 > >限界分支法(實際上沒有剪枝,介紹的是廣度優先搜尋):01揹包問題,佇列實現方式(FIFO)

限界分支法(實際上沒有剪枝,介紹的是廣度優先搜尋):01揹包問題,佇列實現方式(FIFO)

限界分支法:佇列實現方式

前面已經介紹過限界分支法大部分是基於廣度優先搜尋,廣度優先搜尋一般藉助於佇列實現,剪枝的情況可以藉助於優先順序佇列。

實現如下:

#%%
class FIFO_01_Pack:
    def __init__(self,N,V,C,W):
        self.num =N
        self.Volume = V
        self.Cost = C
        self.Value = W
        
        self.BestValue = 0
        
        #用於存放活結點,便於理解,把根結點,以及第0層結束標誌-1放進去
# 結點包括2個屬性:當前空間大小,當前的價值大小 self.queue = [[0,0],[-1,-1],] # 實現時葉子結點不加入到活結點列表,當屬於葉子結點時,增加對結果的處理 def enQueen(self,pair,depth): if depth == self.num -1: CurValue = pair[1] if CurValue > self.BestValue: self.BestValue = CurValue else
: self.queue.append(pair) def pack_01(self): # selected = [0]*self.num # 首先取出根結點 depth = 0 pair = self.queue.pop(0) CurCost = pair[0] CurValue = pair[1] while True: # 判斷左結點能否加入到佇列,能的話,把當前空間和當前價值放入佇列
if CurCost + self.Cost[depth] < self.Volume: self.enQueen([CurCost + self.Cost[depth],CurValue + self.Value[depth]],depth) # 右結點總是可以加入佇列的,因為沒有約束條件的限制 self.enQueen([CurCost,CurValue],depth) # 然後彈出下一個結點 pair = self.queue.pop(0) CurCost = pair[0] CurValue = pair[1] # 當同一層處理完畢時,先判斷是否能夠輸出結果,判斷的標準是佇列是否為空, # 這時下一層的所有結點已經加入了佇列,這時需要把下一層 # 增加一個結尾-1便於判斷,然後進入下一層,彈出下一個結點 if CurCost == -1: if not self.queue: return self.BestValue self.enQueen([-1,-1],depth) depth += 1 pair = self.queue.pop(0) CurCost = pair[0] CurValue = pair[1] def print_Result(self): print(self.pack_01())

Baseline對比及結果輸出:

class pack_01_back_test:        
    def __init__(self,N,V,C,W):
        self.num =N
        self.V = V
        self.C = C
        self.W = W
        self.BestResult = [False]*N
        self.Selected = [False]*N
        self.BestValue = 0
        self.CurCost = 0
        self.CurValue = 0
    
    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:
            if self.CurCost + self.C[depth] <= self.V:
                self.Selected[depth] = True
                
                self.CurCost += self.C[depth]
                self.CurValue  += self.W[depth]
                # next
                self.pack_01_back_tracking(depth+1)
                # undo
                self.CurCost -= self.C[depth]
                self.CurValue  -= self.W[depth]

            self.Selected[depth] = False
            self.pack_01_back_tracking(depth+1)
        
    def print_Result(self):
        self.pack_01_back_tracking(0)
        print(self.BestResult)
        print(self.BestValue)

#%%
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(N,V,C,W).print_Result()


[False, True, True, True, False, True, False, True]
39.0
39.0

追蹤解

追蹤解,上述實現的情況下,解都在最後一層,根本不知道之前的路徑是怎樣的,廣度優先搜尋,同一個緯度,假如不加指標判斷的話,根本不知道最優解是選擇的哪一個,所以需要同一個緯度的每一個結點,記住他之前的路徑,才能在最優解的時候之前是怎麼走過來的,每一個結點用一個數組記錄路徑,這樣實現的感覺消耗有點大啊,通常看見是採用連結串列方式