使用Python和Pulp最大化每日利润问题

问题描述 投票:0回答:1

我在尝试最大化巧克力工厂的每日利润时遇到了麻烦。

问题:最大化生产单块巧克力(每块巧克力有利润和最大每日产能)和特殊包装(每块巧克力包含 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。

我认为结果的差异与特殊包装有关,因为它们取决于每种巧克力。

你能帮我找出我遗漏了什么/做错了什么吗?

谢谢

python pulp
1个回答
0
投票

您走在正确的道路上。您缺少的核心部分是如何获得所生产的每种巧克力的正确总数。

真正需要的是某种机制来询问包装,以获取生产该包装所需的单个巧克力的数量,因为您真正想做的是:

production[choc] = individual_pieces[choc] + quantity_in_packs[choc]

这样,当您执行“针对每种巧克力”类型的约束时,您就会得到正确的总数。下面的解决方案使用字典来实现这一点。实际上是一本字典的字典,这样你就可以问它:

quantity_in_pack[pack][choc]

要获得该数字,那么当您执行“每块巧克力”时,您需要对所有包装进行求和以获得总计(如图所示)。可能还有其他几种方法可以做到这一点......您可能可以翻转字典并按巧克力索引,然后打包,但这似乎合乎逻辑。

无论如何,你实际上只有两个限制:

  1. 生产总数
  2. 每块巧克力的产量

备注:

  • 我留下了一些评论并注释掉了一些不必要的代码
  • 我重新命名了这些集合。对眼睛更轻松,避免
    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
© www.soinside.com 2019 - 2024. All rights reserved.