Python 在对象的组合上有条件地迭代
我正在研究一个小的营养计划,希望找到某些食物的最佳组合,以满足卡路里和大量营养素方面的一些要求。就这个问题而言,我将只关注目标卡路里 我有来自API的数据,可以告诉我关于给定食物的事情。例如:Python 在对象的组合上有条件地迭代,python,Python,我正在研究一个小的营养计划,希望找到某些食物的最佳组合,以满足卡路里和大量营养素方面的一些要求。就这个问题而言,我将只关注目标卡路里 我有来自API的数据,可以告诉我关于给定食物的事情。例如: egg = Food(FOOD_IDS["Boiled Egg"]) print(egg.macros.__dict__) 结果: {'protein_grams': 6.0, 'fat_grams': 4.5, 'carb_grams': 1.0, 'calories': 68.5} 我想做的是找到
egg = Food(FOOD_IDS["Boiled Egg"])
print(egg.macros.__dict__)
结果:
{'protein_grams': 6.0, 'fat_grams': 4.5, 'carb_grams': 1.0, 'calories': 68.5}
我想做的是找到我所能吃的食物的可能组合,这些组合能增加目标数量的卡路里
因此,如果我有鸡蛋
,大米
,鸡肉
,在食物
的集合中,我如何找到这些食物的所有可能组合,比如3000卡路里
我曾考虑使用随机
在循环中不断收集食物,直到达到卡路里目标,但随着食物清单的增加,我担心性能。我以前使用过itertools
库中的产品
、组合
、和置换
,但我不知道这些方法如何在这里应用,因为我打算在边界条件内迭代
以下是我迄今为止的尝试:
def get_daily_food_options(self):
target = 3000
food_combinations = []
groceries = []
groceries.append(Food(FOOD_IDS["Boiled Egg"]))
groceries.append(Food(FOOD_IDS["Brown Rice"]))
groceries.append(Food(FOOD_IDS["Chicken Breast"]))
num_iterations = 100 # I have to manually change this to get more/less results
for i in range(num_iterations):
temp_combination = []
current_calories = 0
while current_calories < target:
food = choice(groceries) # random (not smart)
temp_combination.append(food)
current_calories += food.macros.calories
food_combinations.append(temp_combination)
return food_combinations
下面的程序获取总热量计数和代表单个食品的热量含量的整数列表。它试图找到这些食物的倍数,这些食物加起来正好达到所需的总热量:
from itertools import product
def get_combinations(total_calories, calorie_counts):
n = len(calorie_counts)
max_factors = [total_calories // calorie_counts[i] for i in range(n)]
for t in product(*(range(factors + 1) for factors in max_factors)):
calorie_count = sum([t[i] * calorie_counts[i] for i in range(n)])
if calorie_count == total_calories:
print(t)
get_combinations(3000, [300, 150, 200])
如果您坚持每种食物至少有一份,则将for循环更改为:
for t in product(*(range(1, factors + 1) for factors in max_factors)):
其想法是,如果总热量为X,而某一食物项目的热量为Y,则该食物项目的唯一可能份数在[0..X//Y]范围内(或[1..X//Y],如果您坚持至少有一份)。因此,如果有N个食物项目,程序会通过生成长度为N的元组,对所有可能的食物项目组合进行详尽的试验,其中每个元组是一个可能的组合
这里通常采用的类似里程表的方法是
def combos(menu,target,extra): # menu: list of values
# Put the biggest first for efficiency
# and to avoid large shortfalls:
order=sorted(enumerate(menu),key=lambda e: -e[1])
# Construct inverse permutation:
inv=[None]*len(menu)
for i,(j,_) in enumerate(order): inv[j]=i
for c in combos0([v for _,v in order],target,extra,[]):
yield [c[i] for i in inv]
def combos0(menu,target,extra,pfx):
v=menu[len(pfx)]
# Leave no budget unspent:
if len(pfx)==len(menu)-1:
n=-(-target//v) # ceiling division
if target+extra>=n*v: yield pfx+[n]
else:
for i in range((target+extra)//v+1):
for c in combos0(menu,target-i*v,extra,pfx+[i]): yield c
你在这里的电话是套餐([f.macros.carries for f in foods],3000100)
这个怎么样<代码>当前热量+=总和([f.macros.carries for f in temp\u composition])
可能不是必需的,因为当前热量
具有当前while迭代之前的卡路里数。由于每次while迭代都会将食物添加到temp\u组合中
,因此只需将当前热量
与所选食物的热量相加,即当前热量+=food.macros.carries
。这样,您就可以避免在sum([f.macros.carries for f in temp_composition])
表达式中进行额外的O(n)计算。@Viethran很好,谢谢您研究过线性规划吗?scipy是一个很棒的软件包。它不会直接回答您的问题,但您应该能够找到一个最佳解决方案列表。同样有用:@JacobIRR:目标是“大约3000”,还是需要精确?我真的很喜欢这有一个排序输出。我将把它插入到我的大环境中,看看它是如何工作的我想我知道了max\u factors
在做什么,但是当我用13种食物运行这个时,它运行了很长一段时间(现在已经五分钟了)。这是否意味着我需要通过某种数据科学包/库来运行它,它的数学运算在C级进行了优化?我看不到任何进一步优化它的方法。@JacobIRR好吧,有13种食物,你就有了13个嵌套循环,测试每种可能的食物组合,其中每种食物的分量可以从0变化到其最大允许值(我现在希望我将变量max\u factors
命名为max\u servings
)。它可能会运行很长时间。对于i,(j,u),按顺序
抛出TypeError:“int”对象在Python 3.7中是不可编辑的
。它应该是枚举(顺序)
?@JacobIRR:当然可以;从移动设备发布代码有其局限性。编辑。
def combos(menu,target,extra): # menu: list of values
# Put the biggest first for efficiency
# and to avoid large shortfalls:
order=sorted(enumerate(menu),key=lambda e: -e[1])
# Construct inverse permutation:
inv=[None]*len(menu)
for i,(j,_) in enumerate(order): inv[j]=i
for c in combos0([v for _,v in order],target,extra,[]):
yield [c[i] for i in inv]
def combos0(menu,target,extra,pfx):
v=menu[len(pfx)]
# Leave no budget unspent:
if len(pfx)==len(menu)-1:
n=-(-target//v) # ceiling division
if target+extra>=n*v: yield pfx+[n]
else:
for i in range((target+extra)//v+1):
for c in combos0(menu,target-i*v,extra,pfx+[i]): yield c