我在尝试最大化巧克力工厂的每日利润时遇到了麻烦。
问题:最大化生产单块巧克力(每块巧克力有利润和最大每日产能)和特殊包装(每块巧克力包含 3 块巧克力和利润)的工厂的每日利润。示例:
# Limit Variables
# n - number of chocolates
# p - the number of special packs
# max_chocolates - the total maximum chocolate production
# capacity in a day
n = 5
p = 2
max_chocolates = 150
# For the chocolates: Each chocolate (1,2,3,4,5) has a profit and a maximum daily production capacity
profit_chocolates = [50, 30, 45, 40, 35]
capacity_chocolates = [27, 33, 30, 37, 35]
# For the special packs: Each pack is made of 3 chocolates and a profit for said pack
pack_components = [(1, 3, 5), (2, 3, 4)]
profit_packs = [130, 130]
接下来我给出获取每日最大利润的代码:
# Problem initialization
prob = LpProblem("Maximize_chocolates_and_packs_profit", LpMaximize)
# Decision Variables:
chocolate_vars = [LpVariable(f"x{i}", lowBound=0, cat="Integer") for i in range(n)]
packs_vars = [LpVariable(f"p{i}", lowBound=0, cat="Integer") for i in range(p)]
# Objective Function
prob += lpSum(profit_chocolates[i] * chocolate_vars[i] for i in range(n)) + \
lpSum(profit_packs[i] * packs_vars[i] for i in range(p))
# Constraints:
# For the maximum number of chocolates we are allowed to produce in a day
prob += lpSum(chocolate_vars[i] for i in range(n)) <= max_chocolates
# For the maximum daily production of each chocolate
for i in range(n):
prob += chocolate_vars[i] <= capacity_chocolates[i]
# For the special packs:
# Profit Constraint: The profit of each pack should not be lower or equal to the
# sum of the profits of the chocolates said pack contains
for i in range(p):
prob += lpSum(profit_chocolates[j - 1] for j in pack_components[i]) >= profit_packs[i]
# Capacity Constraint for the packs: Each pack should take into consideration the
# capacity of the chocolates it contains - if the capacity of single chocolate of the pack
# exceeds the daily production limit of that chocolate then we are not able to produce the pack
for i in range(p):
for j in pack_components[i]:
prob += (chocolate_vars[j - 1]) <= capacity_chocolates[j - 1]
# Each decision variable must be greater or equal to 0
for i in range(n):
prob += chocolate_vars[i] >= 0
for i in range(p):
prob += packs_vars[i] >= 0
prob.solve()
for i in range(len(chocolate_vars)):
print(chocolate_vars[i].varValue)
for i in range(len(packs_vars)):
print(packs_vars[i].varValue)
print(f"Maximum daily profit: {int(prob.objective.value())}")
对于上述输入数据,预期结果(最大利润)应该是6440。然而,我得到的结果是6035。
我认为结果的差异与特殊包装有关,因为它们取决于每种巧克力。
你能帮我找出我遗漏了什么/做错了什么吗?
谢谢
您走在正确的道路上。您缺少的核心部分是如何获得所生产的每种巧克力的正确总数。
真正需要的是某种机制来询问包装,以获取生产该包装所需的单个巧克力的数量,因为您真正想做的是:
production[choc] = individual_pieces[choc] + quantity_in_packs[choc]
这样,当您执行“针对每种巧克力”类型的约束时,您就会得到正确的总数。下面的解决方案使用字典来实现这一点。实际上是一本字典的字典,这样你就可以问它:
quantity_in_pack[pack][choc]
要获得该数字,那么当您执行“每块巧克力”时,您需要对所有包装进行求和以获得总计(如图所示)。可能还有其他几种方法可以做到这一点......您可能可以翻转字典并按巧克力索引,然后打包,但这似乎合乎逻辑。
无论如何,你实际上只有两个限制:
备注:
j-1
指数疼痛等range(n)
的东西更容易阅读,所以我喜欢这样做是为了提高可读性,然后你可以相当容易地做类似数学的表达式:for car in cars
等。from pulp import *
# Limit Variables
# n - number of chocolates
# p - the number of special packs
# max_chocolates - the total maximum chocolate production
# capacity in a day
n = 5
chocolates = ['extra dark', 'milk', 'raspberry', 'caramel', 'sprinkles']
p = 2
packs = ['mega deluxe', 'mammas pride']
max_chocolates = 150
# For the chocolates: Each chocolate (1,2,3,4,5) has a profit and a maximum daily production capacity
profit_chocolates = [50, 30, 45, 40, 35]
profit_chocolates = dict(zip(chocolates, profit_chocolates))
capacity_chocolates = [27, 33, 30, 37, 35]
capacity_chocolates = dict(zip(chocolates, capacity_chocolates))
# For the special packs: Each pack is made of 3 chocolates and a profit for said pack
pack_components = [(1, 3, 5), (2, 3, 4)]
# this will be very helpful... break down the pack contents and quantities:
pack_breakdown = {
'mega deluxe' : {chocolates[i-1]: 1 for i in pack_components[0]},
'mammas pride' : {chocolates[i-1]: 1 for i in pack_components[1]}
}
profit_packs = [130, 130]
profit_packs = dict(zip(packs, profit_packs))
# Problem initialization
prob = LpProblem("Maximize_chocolates_and_packs_profit", LpMaximize)
# Decision Variables:
# Let the make_chocolate be JUST for individual sale, not the aggregate number.
make_chocolate = LpVariable.dicts("make_chocolate", chocolates, lowBound=0, cat="Integer")
make_pack = LpVariable.dicts("make_pack", packs, lowBound=0, cat="Integer")
# This is GOOD / Correct
# Objective Function
prob += lpSum(profit_chocolates[c] * make_chocolate[c] for c in chocolates) + \
lpSum(profit_packs[p] * make_pack[p] for p in packs)
# Constraints:
# Need to capture the individual + the total qty from packs
# For the maximum number of chocolates we are allowed to produce in a day
# .values() is used here to sum up the quantities of each type, which
# *could* be more than 1
prob += lpSum(make_chocolate[c] for c in chocolates) \
+ lpSum(make_pack[p] * sum(pack_breakdown[p].values()) for p in packs) \
<= max_chocolates
# For the maximum daily production of each chocolate
# we need to add the individuals + the qty from produced packs...
for c in chocolates:
# we can make a convenient expression to sum up the qty of c in all packs...
qty_in_packs = lpSum(pack_breakdown[p].get(c, 0) * make_pack[p] for p in packs)
prob += make_chocolate[c] + qty_in_packs <= capacity_chocolates[c]
# For the special packs:
# THIS SHOULD NOT BE NEEDED. Your profit expression is good.
# Profit Constraint: The profit of each pack should not be lower or equal to the
# sum of the profits of the chocolates said pack contains
# for i in range(p):
# prob += lpSum(profit_chocolates[j - 1] for j in pack_components[i]) >= profit_packs[i]
# Capacity Constraint for the packs: Each pack should take into consideration the
# capacity of the chocolates it contains - if the capacity of single chocolate of the pack
# exceeds the daily production limit of that chocolate then we are not able to produce the pack
# for i in range(p):
# for j in pack_components[i]:
# prob += (make_chocolate[j - 1]) <= capacity_chocolates[j - 1]
# # Each decision variable must be greater or equal to 0
# for i in range(n):
# prob += make_chocolate[i] >= 0
# for i in range(p):
# prob += make_pack[i] >= 0
prob.solve()
print('Individual chocolate plan:')
for c in chocolates:
print(f' {c: >10}: {make_chocolate[c].varValue}')
print('\nPack plan:')
for p in packs:
print(f' {p: >15}: {make_pack[p].varValue}')
print('\nProduction Limit check (total/limit)')
for c in chocolates:
amount_produced = make_chocolate[c].varValue
amount_produced += sum(pack_breakdown[p].get(c, 0) * make_pack[p].varValue for p in packs)
print(f' {c: >10}: {amount_produced}/{capacity_chocolates[c]}')
print(f"\nMaximum daily profit: {int(prob.objective.value())}")
Result - Optimal solution found
Objective value: 6440.00000000
Enumerated nodes: 0
Total iterations: 0
Time (CPU seconds): 0.00
Time (Wallclock seconds): 0.00
Option for printingOptions changed from normal to all
Total time (CPU seconds): 0.00 (Wallclock seconds): 0.00
Individual chocolate plan:
extra dark: 27.0
milk: 0.0
raspberry: 0.0
caramel: 7.0
sprinkles: 26.0
Pack plan:
mega deluxe: 0.0
mammas pride: 30.0
Production Limit check (total/limit)
extra dark: 27.0/27
milk: 30.0/33
raspberry: 30.0/30
caramel: 37.0/37
sprinkles: 26.0/35
Maximum daily profit: 6440