typora-copy-images-to: Python 编程导论
文章目录
Python 编程导论 Chapter 12 —— 背包与图的最优化问题
-
如果解决问题涉及最大、最小、最多、最少、最快、最低价格等情况,那么你就非常有可能将这个问题转换为一个典型的最优化问题,从而使用已知的计算方法进行解决
-
最优化问题包括:
- 目标函数
- 约束条件集合
-
贪婪算法非常实用
12.1 背包问题
- 条件,窃贼有背包,装20磅赃物,如下图:
- 找出最佳的盗窃方案
12.1.1 贪婪算法
- 无法确保使用贪婪算法找出的解是最优解
class Item(object): # 定义一个Item 的类
def __init__(self, n, v, w): # name value weight 三个属性
self.name = n
self.value = v
self.weight = w
def getName(self):
return self.name
def getValue(self):
return self.value
def getWeight(self):
return self.weight
def __str__(self):
result = '<' + self.name + ', ' + str(self.value)\
+ ', ' + str(self.weight) + '>'
return result
def value(item):
return item.getValue()
def weightInverse(item): # weight 的倒数
return 1.0/item.getWeight()
def density(item): # item 的密度
return item.getValue()/item.getWeight()
def greedy(items, maxWeight, keyFunction):
"""假设Items 是列表,maxWeight >= 0
keyFunctions将物品元素映射为数值"""
itemsCopy = sorted(items, key = keyFunction, reverse = True)
result = []
totalValue,totalWeight = 0.0, 0.0
for i in range(len(itemsCopy)):
if (totalWeight + itemsCopy[i].getWeight()) <= maxWeight:
result.append(itemsCopy[i])
totalWeitht += itemsCopy[i].getWeight()
totalValue += itemsCopy[i].getValue()
return(result, totalValue)
# 只要 keyFunction 定义了 items 中元素的排序规则即可
- greedy 函数的算法的效率取决于sorted的时间复杂度,以及for循环执行的次数
def buildItems():
names = ['clock','painting','radio','vase','book','computer']
values = [175,90,20,50,10,200]
weights = [10,9,4,2,1,20]
Items = []
for i in range(len(values)):
Items.append(Item(names[i], values[i], weight[i]))
return Items
def testGreedy(itmes, maxWeight, keyFunction):
taken, val = greedy(items, maxWeight, keyFunction)
print('Total value of items taken is', val)
for item in taken:
print(" ", item)
def testGreedys(maxWeight = 20):
items = buildItems()
print('Use greedy by value to fill knapsack of size', maxWeight)
testGreedy(items, maxWeight, value)
print('\nUse greedy by weight to fill knapsack of size',
maxWeight)
testGreedy(items, maxWeight, weightInverse)
print('\nUse greedy by density to fill knapsack of size',
maxWeight)
testGreedy(items, maxWeight, density)
12.1.2 0/1 背包问题的最优解
-
0 / 1 背包问题可以做如下定义:
- 每个物品都可以用一个值对<价值,重量>表示;
- 背包能够容纳的物品总重量并超过w;
- 向量I表示可用物品集合
- 向量V表示物品是否被窃贼带走,V[i] = 1,则物品I[i]被带走;如果V[i] = 0,则物品保留
- 目标是找到一个V使得:
的值最大
- 并且满足
的约束条件
-
简单解决问题的办法:
- 枚举所有可能的物品组合,生成所有物品集合的幂集
- 去掉所有超过背包允许重量的物品组合
- 选出剩下价值最大的组合
- 缺陷是子集数量呈现指数型增长
-
背包问题有一个变种,称为分数背包问题,或者连续背包问题。对于这种问题,贪婪算法一
定可以找到最优解。因为物品是无限可分的,所以对于具有最高价值/重量比值的物品来说,肯定拿得越多越好。举例来说,假如我们的窃贼在一间屋子中只发现3种有价值的物品:一袋金粉、一袋银粉和一袋葡萄干。那么在这种情况下,密度贪婪算法肯定能找到最优解
def genPowerset(L):
"""假设L是列表
返回一个列表,包含L中元素所有可能的集合。例如,
如果L=[1, 2],则返回的列表包含元素[1]、[2]和[1,2]"""
powerset = []
for i in range(0, 2**len(L)):
binStr = getBinaryRep(i, len(L))
subset = []
for j in range(len(L)):
if binStr[j] == '1':
subset.append(L[j])
powerset.append(subset)
return powerset
def chooseBest(pset, maxWeight, getVal, getWeight):
bestVal = 0.0
bestSet = None
for items in pset:
itemsVal = 0.0
itemsWeight = 0.0
for item in items:
itemsVal += getVal(item)
itemsWeight += getWeight(item)
if itemsWeight <= maxWeight and itemsVal > bestVal:
bestVal = itemsVal
bestSet = items
return (bestSet, bestVal)
def testBest(maxWeight = 20):
items = buildItems()
pset = genPowerset(items)
taken, val = chooseBest(pset, maxWeight, Item.getValue,
Item.getWeight)
print('Total value of items taken is', val)
for item in taken:
print(item)
12.2 图的最优化问题(涉及图论等内容,略)
- 图是有边连接起来的节点对象的集合,如果边是单向的,则图称之为有向图
- N1 到 N2有一条边,我们称N1 为源节点或父节点,N2 为目标节点,子节点