原标题:遗传算法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

结语

背包问题,应该是我们到现在为止,处理的最复杂的算法问题,因为在背包中的物品,不但可以放入还可以拿出,还需要修改……也就是说我们的基因长度是可以变化的。

另外我们还可以增加其他的条件来增加复杂程度,比如大航海时代的航运问题:船的货物与食物、淡水和船员的配比问题:

货物运太少,亏本,运太多占用其他空间

食物不够,到不了目的地

淡水不够,雷同食物

船员数量要与货物数量相关,太少,处理不了货物,太大又要消耗更多食物淡水。这四种物质相互制约,当然还可以加入航线上哪些地方可以补给食物,可以补给淡水……等等,就更加复杂了。

责任编辑:

python完全背包最优_python 完全背包问题_遗传算法Python实战 009.背包问题相关推荐

  1. python最终目标是什么_Python之父Guido谈Python的未来

    6月初,Python之父Guido van Rossum在今天的PyCon US大会上作了名为"Python Language"的演讲.近日,他又接受了IT媒体Infoworld的 ...

  2. python语言包括哪些实现_Python语言基础考察点:python语言基础常见考题(一)

    一.python是静态还是动态类型?是强类型还是弱类型? 1.动态强类型语言(不少人误以为是弱类型) 不要傻傻分不清 2.动态还是静态指的是编译期还是运行期确定类型 3.强类型指的是不会发生隐式类型转 ...

  3. python海龟作图好看图案_python海龟绘图,其实python也很强大,这些技能你知道吗?...

    Turtle库 简介 什么是Turtle 首先,turtle库是一个点线面的简单图像库,能够完成一些比较简单的几何图像可视化.它就像一个小乌龟,在一个横轴为x.纵轴为y的坐标系原点,(0,0)位置开始 ...

  4. python实现erp系统后端_python开发erp教程《PYTHON编一套完整ERP系统,15万元能下来吗》...

    python开发erp教程<PYTHON编一套完整ERP系统,15万元能下来吗> python开发erp教程2020-10-09 13:08:31人已围观 PYTHON编一套完整ERP系统 ...

  5. python炫酷动画源代码_Python小技巧:如何用Python代码发一个炫酷的朋友圈

    相信很多朋友在发朋友圈的时候,都会发一些话语,有些还会配一张图,但是普通的配图怎么符合我们程序员的身份呢?发出去都觉得没牌面,应该整点技术性的活. 今天给大家带来一个项目,这个小项目只用50行代码就能 ...

  6. python用input输入字典_python输入字典_输入字典python_python用input输入字典 - 云+社区 - 腾讯云...

    广告关闭 腾讯云11.11云上盛惠 ,精选热门产品助力上云,云服务器首年88元起,买的越多返的越多,最高返5000元! 本篇将介绍python里面的字典,更多内容请参考:python学习指南 pyth ...

  7. python语言基础项目报告书_Python语言基础01-初识Python

    1. Python简介 1.1 Python的历史 Python的创始人为吉多·范罗苏姆(荷兰语:Guido van Rossum) 1989年的圣诞节期间:吉多·范罗苏姆为了在阿姆斯特丹打发时间,决 ...

  8. python下载matplotlib.finance模块_Python pyfinance包_程序模块 - PyPI - Python中文网

    PyFinance pyfinance是一个python包,用于投资管理和安全回报分析. 它是对面向量化金融的现有软件包的补充,例如pyfolio, 熊猫数据读取器,以及fecon235 支持巨蟒3. ...

  9. python如何调整图片大小_Python基础进阶 - 如何使用Python调整图像大小

    Python已成为编程语言的首选.不仅适用于一般的面向对象的编程,还适用于各种科学,数学,统计等应用. 由于强大的开发人员社区已经使用Python开发了用于各种目的的库和API,因此所有这些都是可能的 ...

最新文章

  1. Xbox One:微软商店中都是Xbox One的主题
  2. UVA 10603 Fill
  3. 苹果M1芯片升级果然很炸场:性能暴涨功耗速降!但最香新品竟是无降噪AirPods 3...
  4. xxx while the managed IDbConnection interface was being used: Login failed for user xxx
  5. C++ 面向对象(数据封装)
  6. Java 源程序与编译型运行区别
  7. 轮播高度_Qt编写自定义控件24-图片轮播控件
  8. android系统(100)---如何减少lowmemory的发生几率
  9. CSS样式中,background-image 背景图片居中显示并且在不同屏幕分辨率下始终居中
  10. 九九乘法表的简单代码
  11. 给HTML静态网页设置端口号 安装静态服务器
  12. html页面实现pdf打印预览,利用pdfjs在线预览、打印pdf
  13. OPTEE CA和TA通信 实现从java到ca
  14. 鸡兔同笼:35个头,94只脚,问鸡、兔各多少只?
  15. html 字体炫彩特效,CSS3:TEXT-SHADOW|BOX-SHADOW(炫彩字体)
  16. 可能致癌的几种化妆品
  17. CTF-实验吧后台登陆
  18. c语言最新标准c22,【C语言实例】c22-选择分支结构.doc
  19. electron设置淘宝镜像
  20. Transformer T5 模型慢慢读

热门文章

  1. SAP实施商看SAP在我国的发展
  2. 退回余款退回银行会计分录
  3. ABAP-SQL基础知识
  4. SAP 取月度期初库存和月度期末库存(历史库存)
  5. SAP HANA要改变什么?
  6. 几家OA厂商介绍及产品特点总结
  7. smartform 中currency 字段的小数点问题
  8. 摩托罗拉能否追回逝去的那些年?
  9. 大热荒野敲开资本大门后,精致露营就留得住年轻人吗?
  10. 奶源向左配方向右,飞鹤、澳优、贝因美们要如何重振国产奶粉?