部分背包问题是一种经典的贪心问题,物品可以取一部分,也就是可以随意拆分的物品。
算法思路:
算法核心思想:让背包单位空间价值达到最高
注释较为详细,此处不再赘述。
- from myRandom import randomint_LCG
-
-
- def KnaspackGreedy(weights, values, capacity):
- vw_ratios = [] # 各商品性价比
- max_value = 0 # 该背包容量下的最大价值
- # 计算性价比
- for i in range(len(weights)):
- vw_ratio = {} # 字典(记录比率和原索引)用于后续排序
- vw_ratio['ratio'] = round(values[i] / weights[i], 2) # 计算性价比(价值/重量)
- vw_ratio['index'] = i # 设置索引
- vw_ratios.append(vw_ratio) # 将计算该商品的性价比、原索引字典添加到列表中
- print("性价比:{}".format(vw_ratios)) # 打印计算好的各商品性价比、原索引
- vw_ratios.sort(key=compare, reverse=True) # 将性价比数组排序,排序依据列表每项字典中的“ratio”值
- print("性价比(排序):{}".format(vw_ratios)) # 打印排序好的性价比列表
- best_select = [] # 最佳选择的商品序列(取了全部还是部分)
- # 开始装填背包(从高性价比商品开始遍历)
- for vw_ratio in vw_ratios:
- # 如果该商品重量小于当前背包剩余容量
- if weights[vw_ratio['index']] < capacity:
- good = {}
- # 减小背包容量
- capacity -= weights[vw_ratio['index']]
- # 更新当前背包最大价值
- max_value += values[vw_ratio['index']]
- # 将该物品添加到最佳选择队列中
- good['index'] = vw_ratio['index']
- good['weights'] = weights[vw_ratio['index']]
- best_select.append(good)
- else: # 否则背包不能装下该商品全部
- good = {}
- # 将背包剩余容量全部装该商品
- good['weights'] = capacity
- good['index'] = vw_ratio['index']
- max_value += capacity * vw_ratio['ratio']
- # 背包剩余容量清零
- capacity = 0
- # 将该物品添加到最佳选择队列中
- good['index'] = vw_ratio['index']
- max_value += capacity * vw_ratio['ratio']
- best_select.append(good)
- # 如果背包剩余容量清零则终止遍历
- if capacity == 0:
- break
-
- print("最佳商品选择:{}".format(best_select))
- print("背包最大价值:{}".format(max_value))
- return best_select
-
-
- # 自定义排序比较方案
- def compare(onedict):
- return onedict['ratio']
-
-
- if __name__ == '__main__':
- num = 2 # 商品数量
- # 随机生成商品价值和重量
- values = randomint_LCG(num, 0, 50)
- print("价值:{}".format(values))
- weights = randomint_LCG(num, 1, 20)
- print("重量:{}".format(weights))
- # 背包最大容量(问题规模)
- capacity = 10
- print("背包容量:{}".format(capacity))
- KnaspackGreedy(weights, values, capacity)
随机数生成函数(也可以使用自带的random模块改写,笔者此处是从实现随机数底层写)
- import time
-
-
- # 随机数生成器
- def randomint_LCG(length, start, end):
- """线性同余生成器。
- seed -- 随机数的种子
- a -- 线性同余生成器的常数
- c -- 线性同余生成器的常数
- x_0 -- 其实计算点
- length -- 要生成的随机数的数量
- start -- 随机数范围开始的值
- end -- 随机数范围结束的值
- """
- a = int(time.time()) % 54321
- c = int(time.time()) % 12345
- x_0 = int(time.time()) % 78945
- random_numbers = []
- random_numbers.append((a * x_0 + c) % (end - start) + start) # 初始化第一个随机数
- for i in range(1, length):
- random_numbers.append((random_numbers[i - 1] + c) % (end - start) + start) # 计算后续随机数
- return random_numbers
运行测试: