简体   繁体   English

Python-使用加法的所有列表组合

[英]Python - All list combinations using addition

I have a list that is in this form: ["Name/num1/num2/num3/num4/num5", ...] 我有一个这样的形式的列表: ["Name/num1/num2/num3/num4/num5", ...]

For example: available = ["a/1/2/3/4/5", "b/5/4/3/2/4", "c/4/3/2/1/3"] 例如: available = ["a/1/2/3/4/5", "b/5/4/3/2/4", "c/4/3/2/1/3"]

I am trying to create every possible combination of items in available , where the sum of num5 of each item in the combination (eg for a is 5 , for b is 1 , for c is 5 ) is less than MAXNUM (eg 3000). 我试图创建available的每个项目组合,其中组合中每个项目的num5之和(例如for a is 5for b is 1for c is 5 )小于MAXNUM (例如3000)。

To clarify with an example, the program will create a generator for available above and MAXNUM = 9 which can be turned into the following list: 为了用一个例子阐明,程序将创建用于发电机available上述和MAXNUM = 9 ,其可以变成下面的列表:

[["a/1/2/3/4/5", "b/5/4/3/2/4"], ["a/1/2/3/4/5", c/4/3/2/1/3], [b/5/4/3/2/4, b/5/4/3/2/4], [b/5/4/3/2/4, "c/4/3/2/1/3"], ["c/4/3/2/1/3", "c/4/3/2/1/3", "c/4/3/2/1/3"]]

Note: This code needs to return a result with available having 100 items, and MAXNUM = 3000 within a reasonable time (No more than 10 minutes ideally) 注意:此代码需要在合理的时间内(最好不超过10分钟)返回具有100个项目的available结果,且MAXNUM = 3000

Edit: Here is my code in a practical use, as requested: 编辑:这是我的实际要求中的代码:

import itertools
import sys
import time

sys.setrecursionlimit(10000000)

#["Name/Carbs/Protein/Fat/Vitamins/Calories"]
available = ['Fiddleheads/3/1/0/3/80', 'Fireweed Shoots/3/0/0/4/150', 'Prickly Pear Fruit/2/1/1/3/190', 'Huckleberries/2/0/0/6/80', 'Rice/7/1/0/0/90', 'Camas Bulb/1/2/5/0/120', 'Beans/1/4/3/0/120', 'Wheat/6/2/0/0/130', 'Crimini Mushrooms/3/3/1/1/200', 'Corn/5/2/0/1/230', 'Beet/3/1/1/3/230', 'Tomato/4/1/0/3/240', 'Raw Fish/0/3/7/0/200', 'Raw Meat/0/7/3/0/250', 'Tallow/0/0/8/0/200', 'Scrap Meat/0/5/5/0/50', 'Prepared Meat/0/4/6/0/600', 'Raw Roast/0/6/5/0/800', 'Raw Sausage/0/4/8/0/500', 'Raw Bacon/0/3/9/0/600', 'Prime Cut/0/9/4/0/600', 'Cereal Germ/5/0/7/3/20', 'Bean Paste/3/5/7/0/40', 'Flour/15/0/0/0/50', 'Sugar/15/0/0/0/50', 'Camas Paste/3/2/10/0/60', 'Cornmeal/9/3/3/0/60', 'Huckleberry Extract/0/0/0/15/60', 'Yeast/0/8/0/7/60', 'Oil/0/0/15/0/120', 'Infused Oil/0/0/12/3/120', 'Simple Syrup/12/0/3/0/400', 'Rice Sludge/10/1/0/2/450', 'Charred Beet/3/0/3/7/470', 'Camas Mash/1/2/9/1/500', 'Campfire Beans/1/9/3/0/500', 'Wilted Fiddleheads/4/1/0/8/500', 'Boiled Shoots/3/0/1/9/510', 'Charred Camas Bulb/2/3/7/1/510', 'Charred Tomato/8/1/0/4/510', 'Charred Corn/8/1/0/4/530', 'Charred Fish/0/9/4/0/550', 'Charred Meat/0/10/10/0/550', 'Wheat Porridge/10/4/0/10/510', 'Charred Sausage/0/11/15/0/500', 'Fried Tomatoes/12/3/9/2/560', 'Bannock/15/3/8/0/600', 'Fiddlehead Salad/6/6/0/14/970', 'Campfire Roast/0/16/12/0/1000', 'Campfire Stew/5/12/9/4/1200', 'Wild Stew/8/5/5/12/1200', 'Fruit Salad/8/2/2/10/900', 'Meat Stock/5/8/9/3/700', 'Vegetable Stock/11/1/2/11/700', 'Camas Bulb Bake/12/7/5/4/400', 'Flatbread/17/8/3/0/500', 'Huckleberry Muffin/10/5/4/11/450', 'Baked Meat/0/13/17/0/600', 'Baked Roast/4/13/8/7/900', 'Huckleberry Pie/9/5/4/16/1300', 'Meat Pie/7/11/11/5/1300', 'Basic Salad/13/6/6/13/800', 'Simmered Meat/6/18/13/5/900', 'Vegetable Medley/9/5/8/20/900', 'Vegetable Soup/12/4/7/19/1200', 'Crispy Bacon/0/18/26/0/600', 'Stuffed Turkey/9/16/12/7/1500']

global AllSP, AllNames
AllSP = []
AllNames = []

def findcombs(totalNames, totalCarbs, totalProtein, totalFat, totalVitamins, totalNutrients, totalCalories, MAXCALORIES):
    doneit = False
    for each in available:
        each = each.split("/")
        name = each[0]
        carbs = float(each[1])
        protein = float(each[2])
        fat = float(each[3])
        vitamins = float(each[4])
        nutrients = carbs+protein+fat+vitamins
        calories = float(each[5])
#        print(totalNames, totalCalories, calories, each)
        if sum(totalCalories)+calories <= MAXCALORIES:
            doneit = True
            totalNames2 = totalNames[::]
            totalCarbs2 = totalCarbs[::]
            totalProtein2 = totalProtein[::]
            totalFat2 = totalFat[::]
            totalVitamins2 = totalVitamins[::]
            totalCalories2 = totalCalories[::]
            totalNutrients2 = totalNutrients[::]

            totalNames2.append(name)
            totalCarbs2.append(carbs)
            totalProtein2.append(protein)
            totalFat2.append(fat)
            totalVitamins2.append(vitamins)
            totalCalories2.append(calories)
            totalNutrients2.append(nutrients)
#            print("    ", totalNames2, totalCarbs2, totalProtein2, totalFat2, totalVitamins2, totalNutrients2, totalCalories2)
            findcombs(totalNames2, totalCarbs2, totalProtein2, totalFat2, totalVitamins2, totalNutrients2, totalCalories2, MAXCALORIES)
        else:
            #find SP
            try:
                carbs    = sum([x * y for x, y in zip(totalCalories, totalCarbs)])    / sum(totalCalories)
                protein  = sum([x * y for x, y in zip(totalCalories, totalProtein)])  / sum(totalCalories)
                fat      = sum([x * y for x, y in zip(totalCalories, totalFat)])      / sum(totalCalories)
                vitamins = sum([x * y for x, y in zip(totalCalories, totalVitamins)]) / sum(totalCalories)
                balance  = (carbs+protein+fat+vitamins)/(2*max([carbs,protein,fat,vitamins]))
                thisSP   = sum([x * y for x, y in zip(totalCalories, totalNutrients)]) / sum(totalCalories) * balance + 12
            except:
                thisSP = 0
            #add SP and names to two lists
            AllSP.append(thisSP)
            AllNames.append(totalNames)

def main(MAXCALORIES):
    findcombs([], [], [], [], [], [], [], MAXCALORIES)
    index = AllSP.index(max(AllSP))
    print()
    print(AllSP[index], "  ", AllNames[index])

for i in range(100, 3000, 10):
    start = time.time()
    main(i)
    print("Calories:", i, ">>> Time:", time.time()-start)

Right, so the task for N foods has time and space complexity of O(exp(N)) . 是的,所以N食物的任务在时间和空间上的复杂度为O(exp(N)) What you need is some heuristic search like A* (link) which follows some idea of 'how good' one incomplete combination is to direct its further search. 您需要的是一些启发式搜索(例如A *(链接)) ,该搜索遵循某种“不完全组合”是“直接”进行进一步搜索的想法。 As a result you will find not the best solution, but a practical good solution within limited time. 结果,您将不会找到最佳的解决方案,而是在有限的时间内找到切实可行的解决方案。 Alternatives are genetic algorithms, simulated annealing, and other optimization algorithm. 替代方法是遗传算法,模拟退火和其他优化算法。 Note that you must define a metric of how good each combination is! 注意,您必须定义每个组合的好坏的度量!

I was using the package astar ( https://github.com/jrialland/python-astar ) in my video game AI, and it exceeded my expectations. 我在视频游戏AI中使用了astar包( https://github.com/jrialland/python-astar ),它超出了我的期望。

Further suggestions: use namedtuple to make your code more readable, otherwise you will hate finding bugs and extending it: 进一步的建议:使用namedtuple使代码更具可读性,否则您将不喜欢查找错误并扩展它:

from collections import namedtuple

food = namedtuple('Food', 'name carbs protein fat vitamins calories')

bananas = food('bananas', 10, 15, 20, 10, 100)
oranges = food('oranges', carbs=10, protein=15, fat=20, vitamins=10, calories=100)
print(bananas.calories, oranges.fat)

Given the high number of possibilities, perhaps you should approach the use of this information differently. 鉴于可能性很大,也许您应该以不同的方式使用此信息。 For example, if the usage context is selection of foods given a prior selection, you could simply provide the information on how many of each food type can be had without going over the maximum 例如,如果使用情况是事先选择食物的选择,则可以简单地提供有关每种食物可以食用多少的信息,而不必超过最大值

foofInfo = [ food.split("/") for food in available ]
foofInfo = { food[0]:tuple([int(v) for v in food[1:]]) for food in available } #name:(carbs,proteins,fat,vitamins,nutrients,calories)

calFood = {}
for name,(_,_,_,_,calories) in foofInfo.items():
    if calories not in calFood: calFood[calories] = []
    calFood[calories].append(name)

maxCalories = 3000
for calories,foods in calFood.items():
    maxCount = maxCalories//calories
    print("Up to ",maxCount," of ",", ".join(foods))

So you could propose a progressive refinement of the available options rather than a bazillion combinations: 因此,您可以提议对可用选项进行逐步完善,而不要使用无数组合:

Up to  37  of  Fiddleheads, Huckleberries
Up to  20  of  Fireweed Shoots
Up to  15  of  Prickly Pear Fruit
Up to  33  of  Rice
Up to  25  of  Camas Bulb, Beans, Oil, Infused Oil
Up to  23  of  Wheat
Up to  15  of  Crimini Mushrooms, Raw Fish, Tallow
Up to  13  of  Corn, Beet
Up to  12  of  Tomato
Up to  12  of  Raw Meat
Up to  60  of  Scrap Meat, Flour, Sugar
Up to  5  of  Prepared Meat, Raw Bacon, Prime Cut, Bannock, Baked Meat, Crispy Bacon
Up to  3  of  Raw Roast, Basic Salad
Up to  6  of  Raw Sausage, Camas Mash, Campfire Beans, Wilted Fiddleheads, Charred Sausage, Flatbread
Up to  150  of  Cereal Germ
Up to  75  of  Bean Paste
Up to  50  of  Camas Paste, Cornmeal, Huckleberry Extract, Yeast
Up to  7  of  Simple Syrup, Camas Bulb Bake
Up to  6  of  Rice Sludge, Huckleberry Muffin
Up to  6  of  Charred Beet
Up to  5  of  Boiled Shoots, Charred Camas Bulb, Charred Tomato, Wheat Porridge
Up to  5  of  Charred Corn
Up to  5  of  Charred Fish, Charred Meat
Up to  5  of  Fried Tomatoes
Up to  3  of  Fiddlehead Salad
Up to  3  of  Campfire Roast
Up to  2  of  Campfire Stew, Wild Stew, Vegetable Soup
Up to  3  of  Fruit Salad, Baked Roast, Simmered Meat, Vegetable Medley
Up to  4  of  Meat Stock, Vegetable Stock
Up to  2  of  Huckleberry Pie, Meat Pie
Up to  2  of  Stuffed Turkey

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM