Python 编程导论 Chapter 12 —— 背包与图的最优化问题

本文深入探讨Python编程中的背包问题和图的最优化问题,特别是0/1背包问题及其贪婪算法。文章阐述了目标函数和约束条件在最优化问题中的角色,并分析了贪婪算法在求解过程中的效率。此外,还简要提到了图的最优化问题,如有向图等概念。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >


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 为目标节点,子节点
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值