python完全背包最优_python 完全背包问题_遗传算法Python实战 009.背包问题
原標題:遺傳算法Python實戰 009.背包問題
寫在前面的話
以下部分內容,來自百度
背包問題(Knapsack problem)是一種組合優化的NP完全問題。問題可以描述為:給定一組物品,每種物品都有自己的重量和價格,在限定的總重量內,我們如何選擇,才能使得物品的總價格最高。
問題的名稱來源于如何選擇最合適的物品放置于給定背包中。相似問題經常出現在商業、組合數學,計算復雜性理論、密碼學和應用數學等領域中。也可以將背包問題描述為決定性問題,即在總重量不超過W的前提下,總價值是否能達到V?
背包問題里面可以附加各種限制,比如容器的大小、容量、形狀等等,我們在這里不做特別復雜的設定,僅做重量限制,也就是說可以認為你放進去的物品是可以不受形狀的限制,僅受重量和容積的限制,比如你要放入的是糖或者鹽這類東西。
下面是我們要放入背包中的物品:
物品名稱
價值
重量(kg)
容量(L)
面粉
1680
0.265
0.41
黃油
1440
0.5
0.13
糖
1840
0.441
0.29進入算法實現部分
首先,定義個物品類,用來記錄物品的各種屬性:
cla***esource:
def__init__( self, name, value, weight, volume):
self.Name = name
self.Value = value
self.Weight = weight
self.Volume = volume
然后就可以設定物品信息和條件了:設定背包的最大承載重量為10kg,最大容量為4L
items = [Resource( " 面粉", 1680, 0.265, .41),
Resource( " 黃油", 1440, 0.5, .13),
Resource( " 糖", 1840, 0.441, .29)]
maxWeight = 10
maxVolume = 4
我們的目標是在這些限制條件下,將背包里的東西價值最大化。
我們可以想想,如何通過完成這個目標。
首先我們當然是希望價值與重量比和價值與體積比最優的物品優先——這樣我們可以得到盡可能高的總價值。
當我們不能再把性價比最好的資源塞進去的時候,我們就要考慮用下一個最有價值的資源填滿剩余的空間,以此類推。
一旦背包裝滿了,我們還需要考慮,我們是否可以用其他類型的項目組合來代替一種類型的項目,以便于最大限度的增加背包內物品的總價值。
然后定義一個用于記錄當前背包中資源信息的類:
classItemQuantity:
def__init__( self, item, quantity):
self.Item = item
self.Quantity = quantity
def__eq__( self, other):
returnself.Item == other.Item andself.Quantity == other.Quantity
定義健壯性類:背包算法里面,肯定是輕、小、貴為最優,所以需要重量、容積和價值三個成員變量。在對比的時候,優先對比價值,價值相同的時候情況下,對比重量,最后對比容量。
classFitness:
def__init__( self, totalWeight, totalVolume, totalValue):
self.TotalWeight = totalWeight
self.TotalVolume = totalVolume
self.TotalValue = totalValue
def__gt__( self, other):
ifself.TotalValue != other. TotalValue:
returnself.TotalValue > other.TotalValue
ifself.TotalWeight != other. TotalWeight:
returnself.TotalWeight < other.TotalWeight
returnself.TotalVolume < other.TotalVolume
def__str__( self):
return" 重量: {:0.2f} 容積: {:0.2f} 價值: {}".format(
self.TotalWeight,
self.TotalVolume,
self.TotalValue)
健壯性驗證方法,把里面所有的值都累積起來。
defget_fitness(genes):
totalWeight= 0
totalVolume= 0
totalValue= 0
foriq in genes:
count= iq.Quantity
totalWeight+= iq.Item.Weight * count
totalVolume+= iq.Item.Volume * count
totalValue+= iq.Item.Value * count
returnFitness(totalWeight, totalVolume, totalValue)
我們還需要把每個物品的數量都控制在有效的范圍內:
def max_quantity( item, maxWeight, maxVolume):
returnmin(int(maxWeight / item.Weight)
ifitem.Weight > 0elsesys.maxsize,
int(maxVolume / item.Volume)
ifitem.Volume > 0elsesys.maxsize)
下面這個方法是用來創建初代基因的,在基因里面添加一個ItemQuantity,用來記錄剩余的重量和體積,這樣我們就不會出現超限的問題了。而在創建初代基因的時候,我們盡可能的多選取一些物品,這樣先把我們的背包填滿,如果這些物品效果不好,我們就對他們進行替換。
def create(items, maxWeight, maxVolume):
genes = []
remainingWeight, remainingVolume = maxWeight, maxVolume
fori inrange(random.randrange( 1, len(items))):
newGene= add(genes, items, remainingWeight, remainingVolume)
ifnewGeneis not None:
genes.append( newGene)
remainingWeight -= newGene.Quantity * newGene.Item.Weight
remainingVolume -= newGene.Quantity * newGene.Item.Volume
returngenes
上面的創建初代基因的方法里面的內部方法add,作用就是在往背包里面增加東西的時候,計算背包信息的。里面調用了上面那個max_quantity方法,來控制背包里面的剩余空間,不至于超限。在添加一個物品的時候,盡量去選擇還沒有被加進來,避免同一組物品太多。
defadd(genes,items,maxWeight, maxVolume):
usedItems = {iq. Itemfor iq in genes}
item= random.choice( items)
whileitemin usedItems:
item= random.choice( items)
maxQuantity = max_quantity( item,maxWeight, maxVolume)
return ItemQuantity(item,maxQuantity) ifmaxQuantity > 0elseNone
下面是進化函數:里面代碼的意義,見注釋:
def mutate(genes, items, maxWeight, maxVolume, window):
# 滑動窗口
window.slide
# 首先計算傳入的父本基因的健壯性
fitness = get_fitness(genes)
# 計算本次進化,還可以使用的剩余重量和容積
remainingWeight = maxWeight - fitness.TotalWeight
remainingVolume = maxVolume - fitness.TotalVolume
# 用目前父本基因的數量+10 分之一的概率,來控制是否要進行移動窗口賦值
removing = len(genes) > 1andrandom.randint( 0, 10) == 0
# 如果父本基因數量大于1 ,而且隨機概率90% 的話
# 從父本基因里面隨機挑選一個物品,然后把重量和容積累加給背包剩余的空間
# 接下去把這個物品從基因庫中移除掉
ifremoving:
index = random.randrange( 0, len(genes))
iq = genes[index]
item= iq.Item
remainingWeight += item.Weight * iq.Quantity
remainingVolume += item.Volume * iq.Quantity
del genes[index]
# 如果剩余空間還有,而且基因庫中的物品數量已經等于0
# 或者基因庫中的物品數量小于背包里面的物品數量,
# 并且獲得百分之一的概率
# 這個變量用于控制,是否還要往包里面增加物品的。
adding = (remainingWeight > 0orremainingVolume > 0) and
( len(genes) == 0or
( len(genes) < len( items) andrandom.randint( 0, 100) == 0))
# 如果背包里面還可以塞東西,則直接把物品add 到基因組中
ifadding:
newGene = add(genes, items, remainingWeight, remainingVolume)
ifnewGene is notNone:
genes.append(newGene)
return
# 隨機在基因組里面挑選一件物品,加入到背包中
index = random.randrange( 0, len(genes))
iq = genes[index]
item= iq.Item
remainingWeight += item.Weight * iq.Quantity
remainingVolume += item.Volume * iq.Quantity
# 是否要改變基因庫
# 如果基因組的數量小于基因庫中是數量,而且符合25% 的概率
# 從基因庫里面選擇更多的物品填入背包中
# 此過程主要是控制背包里面盡量所有的物品都要有,而不僅是一個類別的物品
changeItem = len(genes) < len( items) andrandom.randint( 0, 4) == 0
ifchangeItem:
itemIndex = items.index(iq.Item)
start= max( 1, itemIndex - window.Size)
stop= min( len( items) - 1, itemIndex + window.Size)
item= items[ random.randint( start, stop)]
# 計算重量,如果還沒有填滿,則把這個物品加入到背包里面去
# 否則把這個物品從背包里面刪掉。
maxQuantity = max_quantity( item, remainingWeight, remainingVolume)
ifmaxQuantity > 0:
genes[index] = ItemQuantity( item, maxQuantity
ifwindow.Size > 1elserandom.randint( 1, maxQuantity))
else:
del genes[index]
下面是一些初始化方法和內部默認方法:
初始化終止條件(這里已經手動指定終止條件了,實際上你是可以通過代碼修改來實現不同組合的,不過get_fiteness方法也要相應修改)
optimal = get_fitness(
[ItemQuantity( items[ 0], 1),
ItemQuantity( items[ 1], 14),
ItemQuantity( items[ 2], 6)])
構造幾個內部方法,Python支持函數指針,可以把整個方法當成參數傳遞過來:
def _mutate_custom( parent, custom_mutate, get_fitness):
childGenes = parent.Genes [:]
custom _mutate( childGenes)
fitness = get _fitness( childGenes)
return Chromosome( childGenes, fitness)
進化過程:
_get_improvement方法的說明,見上一節魔術方塊。
defget_best(get_fitness, targetLen, optimalFitness, geneSet, display,
custom_mutate=None, custom_create=None, maxAge=None) :
ifcustom_mutate isNone:
deffnMutate(parent):
return_mutate(parent, geneSet, get_fitness)
else:
deffnMutate(parent):
return_mutate_custom(parent, custom_mutate, get_fitness)
ifcustom_create isNone:
deffnGenerateParent:
return_generate_parent(targetLen, geneSet, get_fitness)
else:
deffnGenerateParent:
genes = custom_create
returnChromosome(genes, get_fitness(genes))
forimprovement in_get_improvement(fnMutate, fnGenerateParent, maxAge):
display(improvement)
ifnotoptimalFitness > improvement.Fitness:
returnimprovement
def_get_improvement(new_child, generate_parent, maxAge):
parent = bestParent = generate_parent
yieldbestParent
historicalFitnesses = [bestParent.Fitness]
whileTrue:
child = new_child(parent)
ifparent.Fitness > child.Fitness:
ifmaxAge isNone:
continue
parent.Age += 1
ifmaxAge > parent.Age:
continue
index = bisect_left(historicalFitnesses, child.Fitness, 0,
len(historicalFitnesses))
proportionSimilar = index / len(historicalFitnesses)
ifrandom.random < exp(-proportionSimilar):
parent = child
continue
bestParent.Age = 0
parent = bestParent
continue
ifnotchild.Fitness > parent.Fitness:
child.Age = parent.Age + 1
parent = child
continue
child.Age = 0
parent = child
ifchild.Fitness > bestParent.Fitness:
bestParent = child
yieldbestParent
historicalFitnesses.append(bestParent.Fitness)
執行測試的方法:
這方法里面利用了大量的函數指針,通過封裝內部方法的傳遞的方式,封裝和初始化了大量的默認參數,是Python中的一個特性,大家有興趣的話,請自行了解Python的語法特征deffill_knapsack:
startTime = datetime.datetime.now
window = Window( 1,
max( 1, int(len(items) / 3)),
int(len(items) / 2))
sortedItems = sorted(items, key= lambdaitem: item.Value)
deffnDisplay(candidate):
display(candidate, startTime)
deffnGetFitness(genes):
returnget_fitness(genes)
deffnCreate:
returncreate(items, maxWeight, maxVolume)
deffnMutate(genes):
mutate(genes, sortedItems, maxWeight, maxVolume, window)
best = get_best(fnGetFitness, None, optimal, None,
fnDisplay, fnMutate, fnCreate, maxAge= 50)
執行:
fill_knapsack
輸出:
下面是我的一次執行,可以看見,不管初始化的情況怎么樣,最后都能逼近達到我們給出來的終止條件。13x 糖重量: 5.73容積: 3.77價值: 239200: 00: 00
10x 糖, 2x 面粉, 2x 黃油重量: 5.94容積: 3.98價值: 246400: 00: 00.002989
9x 糖, 6x 黃油重量: 6.97容積: 3.39價值: 252000: 00: 00.004021
9x 糖, 8x 黃油重量: 7.97容積: 3.65價值: 280800: 00: 00.004021
10x 糖, 8x 黃油重量: 8.41容積: 3.94價值: 299200: 00: 00.004984
12x 黃油, 7x 糖重量: 9.09容積: 3.59價值: 301600: 00: 00.004984
13x 黃油, 7x 糖重量: 9.59容積: 3.72價值: 316000: 00: 00.005983
15x 黃油, 4x 糖, 2x 面粉重量: 9.79容積: 3.93價值: 323200: 00: 00.021939
15x 黃油, 5x 糖, 1x 面粉重量: 9.97容積: 3.81價值: 324800: 00: 00.042882
15x 黃油, 5x 糖, 1x 面粉重量: 9.97容積: 3.81價值: 324800: 00: 00.178553
14x 黃油, 6x 糖, 1x 面粉重量: 9.91容積: 3.97價值: 328800: 00: 00.184537
14x 黃油, 6x 糖, 1x 面粉重量: 9.91容積: 3.97價值: 328800: 00: 00.383970
結語
背包問題,應該是我們到現在為止,處理的最復雜的算法問題,因為在背包中的物品,不但可以放入還可以拿出,還需要修改……也就是說我們的基因長度是可以變化的。
另外我們還可以增加其他的條件來增加復雜程度,比如大航海時代的航運問題:船的貨物與食物、淡水和船員的配比問題:
貨物運太少,虧本,運太多占用其他空間
食物不夠,到不了目的地
淡水不夠,雷同食物
船員數量要與貨物數量相關,太少,處理不了貨物,太大又要消耗更多食物淡水。這四種物質相互制約,當然還可以加入航線上哪些地方可以補給食物,可以補給淡水……等等,就更加復雜了。
責任編輯:
與50位技術專家面對面20年技術見證,附贈技術全景圖總結
以上是生活随笔為你收集整理的python完全背包最优_python 完全背包问题_遗传算法Python实战 009.背包问题的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: java单链表逆序输出_在数据结构单链表
- 下一篇: python的程序入口地址_第一个Pyt