一、背包问题

01背包是在M件物品取出若干件放在空间为W的背包里,每件物品的体积为W1,W2至Wn,与之相对应的价值为P1,P2至Pn。01背包是背包问题中最简单的问题。01背包的约束条件是给定几种物品,每种物品有且只有一个,并且有权值和体积两个属性。在01背包问题中,因为每种物品只有一个,对于每个物品只需要考虑选与不选两种情况。如果不选择将其放入背包中,则不需要处理。如果选择将其放入背包中,由于不清楚之前放入的物品占据了多大的空间,需要枚举将这个物品放入背包后可能占据背包空间的所有情况。

二、求解思路

  当遇到这样的问题,我们可以换一种角度去思考,假设在一个100m3的房子里面,现在要将房子装满,同时要保证放入的物品个数最多以及装入的东西最重,现在身边有铁球和棉花,请问大家是放铁球进去好呢还是放棉花进去好呢?显而易见,放入铁球进去是最优选择。但是原因是什么呢?很简单,就是因为铁球的密度较大,相同体积的铁球和棉花相比,铁球更重。 
  不过前提是放入第一个铁球时,铁球的体积V1小于等于100m3 ;放入第二个铁球时,铁球的体积V2 小于等于(100-V1)m3;……;放入第n个铁球时,铁球的体积小于等于(100- ∑n1Vn-1)m3 ,要是第n个铁球的体积大于(100- ∑n1Vn-1)m3 ,还真是不如放点单位体积更轻的棉花进去,说的极端点就是所有铁球的体积都大于100m3 ,还真不如随便放入点棉花进去合算。所以总是放铁球进去,不考虑是否放入棉花,容易产生闲置空间,最终会得不到最优选择,可能只是最优选择的近似选择。 
  现在再次回到背包问题上,要使得背包中可以获得最大总价值的物品,参照铁球的例子我们可以知道选择单位重量下价值最高的物品放入为最优选择。但是由于物品不可分割,无法保证能将背包刚好装满,最后闲置的容量无法将单位重量价值更高的物品放入,此时要是可以将单位重量价值相对低的物品放入,反而会让背包的总价值和单位重量的价值更高。假设现在背包的剩余总重量为5kg,存在一个4kg价值为4.5的物品,一个3kg价值为3的物品,一个2kg价值为2的物品,很显然将3kg和2kg的物品放入背包中所获得的价值更高,虽然没有4kg的物品单位重量的价值高。因此通过贪心算法求解01背包的问题可能得不到问题的最优解,得到的是近似最优解的解。 
  创建一个物品对象,分别存在价值、重量以及单位重量价值三种属性。

  以下以单位重量价值角度分析:

import json

def dictsum(list, keyname):
    num = 0
    for item in list:
        num += item[keyname]
    return num

class Greedy():
    def __init__(self,data,maxWeight):
        self.maxWeight=maxWeight
        self.dataList=sorted(self.readData(data), key=lambda e: e.__getitem__('average'), reverse=True)
        self.selectedList=[]
    def readData(self,data):
        for item in data:
            value=item["price"]/item["weight"]
            item.setdefault("average", value)
        return data
    def pick(self):
        for i in range(len(self.dataList)-1):
            tempList=[]
            totleWeight = self.maxWeight
            for j in range(i,len(self.dataList)):
                if self.dataList[j]["weight"]<=totleWeight:
                    tempList.append(self.dataList[j])
                    totleWeight=totleWeight-self.dataList[j]["weight"]
            if tempList!=[]:
                if dictsum(tempList,"price")>dictsum(self.selectedList,"price"):
                    self.selectedList = tempList
                elif dictsum(tempList,"price")==dictsum(self.selectedList,"price"):
                    if dictsum(tempList,"price")<dictsum(self.selectedList,"price"):
                        self.selectedList = tempList
                tempList = []
        return self.selectedList,dictsum(self.selectedList,"weight"),dictsum(self.selectedList,"price")

class Genetic():
    def __init__(self):
        pass

if __name__ == "__main__":
    # 贪婪算法求解01背包问题
    data = [
        {"weight": 4, "price": 4},
        {"weight": 2, "price": 1.9},
        {"weight": 3, "price": 2.9},
    ]
    maxWeight = 5
    selected, subweight, subprice = Greedy(data, maxWeight).pick()
    result = json.dumps([{'被选项目': selected, '总重量': subweight, '总价值': subprice}])