Pulp LP Minimization制定“选择一种类型”约束

时间:2017-05-04 13:11:01

标签: python pulp

以下代码用于运行LP最小化问题,其中我们有某些食物,它们的营养价值和成本。该代码目前在所呈现的状态下工作。我想添加一种类型的约束。我把所有的食物分成了他们的类别(早餐,午餐,晚餐,小吃)。我想创建一个约束,其中只有1 可以选择早餐,午餐和晚餐。 (零食无限制)。 “1”和“0”对应于项目是否(早餐,午餐,晚餐或小吃),具体取决于它在阵列中的位置。

from pulp import *

Food = ["Bacon", "Eggs", "Pancakes", "Waffles", "Yogurt", "Bagels", "Sausage", "Cheerios",
    "Strawberries", "Milk", "OJ", "Oranges", "Apples", "Carrots", "Broccoli","Ham", "Turkey",
    "Steak", "Salmon", "Pasta","Chicken", "Pizza", "Rice", "Salad", "Potatoes"]

nutrition = ["Calories", "Protein", "Sugars", "Cholesterol", "Vitamin_A", "Vitamin_B", "Vitamin_C",
         "Vitamin_K", "Vitamin_E", "Zinc", "Iron", "Fat", "Sodium", "Carbs", "Fiber",
         "Calcium", "Potassium", "Folic_acid", "Thiamin"]

Category = ["Breakfast", "Lunch", "Dinner", "Snack"]

VarCategory = [[1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
               [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 1, 1, 0, 1, 0],
               [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 0, 1, 0, 1],
               [0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]]
VarCategory = makeDict([Category, Food], VarCategory)


VarNutrition = [[45, 367, 84.3, 212, 250, 72.3, 150, 103, 49, 100, 134, 85.1, 52.8, 5.3, 30.9, 290, 280, 412, 159, 288, 231, 324, 428, 370, 403],
            [3, 24, 2.3, 5.3, 10.7, 2.8, 6, 3.2, 1, 8, 1, 1.3, 0.3, 1.1, 2.6, 18, 18, 21, 24.9, 12, 43.4, 13.9, 19.2, 20, 13.7],
            [0, 4, 0, 5.2, 46.7, 0, 1, 1.1, 7, 13, 23.3, 16.9, 11.1, 0.7, 1.5, 6, 5, 0, 0, 11, 0, 4.1, 13.8, 1, 0],
            [3, 86, 7, 0, 3, 2, 10, 0, 0, 3, 0, 0, 0, 0, 0, 8, 7, 61, 10, 10, 40, 9, 18, 13, 7],
            [0, 23, 2, 20, 2, 1, 4, 16, 0, 10, 2, 8, 1, 41, 11, 6, 6, 0, 2, 10, 1, 6, 41, 10, 34],
            [0, 20, 1, 19, 12, 1, 0, 27, 2, 0, 31, 2.5, 1, 1, 4, 0, 0, 50, 50, 12, 25, 10, 23, 0, 22],
            [0, 1, 1, 2, 3, 0, 0, 11, 149, 0, 62, 139, 7, 1, 135, 35, 35, 0, 0, 10, 0, 0, 17, 30, 81],
            [0, 11, 0,  0,  0,  0,  0,  1,  4,  0,  0,  0,  1,  2,  116, 0, 0,  4,  0,  10, 1,  8,  16, 0,  0],
            [0, 12, 0,  0,  0,  0,  0,  1,  2,  0,  0,  2,  0,  0,  4,  0, 0,   4,  9,  10, 2,  6,  4,  0,  0],
            [0, 15, 1,  3,  12, 1,  0,  30, 1,  0,  0,  1,  0,  0,  2,  0,  0, 69,  3,  10, 9,  10, 9,  0,  14],
            [0, 15, 4,  20, 1,  6,  4,  49, 3,  0,  2,  1,  0,  1,  4,  20, 20, 28, 6,  10, 8,  16, 7,  8,  18],
            [6, 41, 5,  11, 4,  1,  22, 3,  1,  4,  0,  1,  0,  0,  1,  8,  7, 25,  9,  20, 8,  19, 8,  29, 33],
            [6, 26, 7,  12, 6,  5,  15, 8,  0,  5,  0,  0,  0,  0,  1,  53, 42, 3,  44, 50, 4,  25, 47, 59, 20],
            [0, 2,  4,  11, 16, 5,  0,  7,  4,  4,  11, 7,  5,  0,  2,  15, 15, 0,  0,  10, 0,  13, 25, 10, 16],
            [0, 0,  0,  8,  0,  2,  0,  11, 12, 0,  2,  18, 6,  2,  9,  16, 16, 0,  0,  30, 0,  7,  10, 0,  0],
            [0, 16, 8,  4,  37, 0,  2,  11, 2,  30, 0,  8,  1,  0,  4,  6,  6, 1,   1,  0,  2,  15, 4,  4,  34],
            [0, 9,  1,  4,  14, 1,  0,  5,  7,  0,  3,  9,  3,  1,  8,  0,  0, 14,  7,  10, 10, 6,  12, 0,  41],
            [0, 17, 3,  10, 6,  6,  0,  68, 9,  0,  2,  8,  0,  1,  14, 0,  0, 5, 1,    20, 1,  0,  15, 0,  15],
            [0, 8,  5,  21, 6,  9,  0,  36, 2,  0,  63, 12, 1, 0,   4,  0,  0, 9,   2,  10, 7,  14, 17, 0,  18]]
VarNutrition = makeDict([nutrition, Food], VarNutrition)

ConstraintsLow = [2000, 72, 0, 85, 100, 100, 100, 100, 100, 0, 0, 0, 0, 90, 100, 100, 100, 100, 100]
ConstraintsLow = makeDict([nutrition],ConstraintsLow)



Cost = [1.22,   1.56,   6.79,   6.79,   1.00,   2.50,   2.00,   0.14,   1.37,   1.69, 1.99, 0.50,   0.50,   0.50,   0.50,   4.25,   4.25,   4.00,   5.00,   7.00, 3.18, 1.25,   5.00,   6.00,   3.00]
Cost = makeDict([Food], Cost)

prob = LpProblem("Nutrition Calculator", LpMinimize)

vars = LpVariable.dicts("Servings of", (Food), 0, None, LpContinuous)
Svars = LpVariable.dicts("Food Chosen", (Category, Food), 0, None, LpBinary)

prob += lpSum(vars[i]*Cost[i] for i in Food )

for j in nutrition:
    prob += lpSum([vars[i]*VarNutrition[j][i] for i in Food]) >= ConstraintsLow[j]


for i in Food:
    prob += vars[i] >= 0
    prob += vars[i] <= 2




print (prob)
prob.writeLP("Nutrition.lp")
prob.solve()
print ("Status:", LpStatus[prob.status])
for v in prob.variables():
    print (v.name, "=", v.varValue)
print ("Total Cost = ", value(prob.objective))

我遇到的问题是创建这样的约束。我想使用二进制变量,但我不知道如何实现它。任何帮助将不胜感激

1 个答案:

答案 0 :(得分:0)

你应该做的是为每种类型的食物选择二元变量,然后是它们之和为1的约束 - 意味着其中一个二元变量是1而其他的是0。

问题在于,例如,使早餐二进制变量打开意味着线性程序中存在if-then条件。如果选择了至少一个早餐项目,那么早餐是1,否则为0.一个if-then语句不是线性的,所以我们需要一种聪明的方法来使它成为线性。我们可以用“大M约束”来做到这一点。

使python变量代表每种食物类型的决策总和,例如: breakfast_sumlunch_sum

然后制作PuLP二元变量breakfast_binarylunch_binary等。

breakfast_binary大于0时,我们将使用大M约束使breakfast_sum“翻转”。然后我们将使用另一个约束来确保二进制变量的总和&lt; = 1.

M基本上是一个很大的数字。它有多大?请注意,您将永远不会分配超过2份每份早餐食品,因此请尝试制作M = 2 * {早餐食品数量}。现在看看这个约束:

M * breakfast_binary >= breakfast_sum

如果breakfast_sum为0,则breakfast_binary 允许为0.只要您分配一份早餐项目,breakfast_binary就会被强制执行翻到1。

用午餐,晚餐等做这个,然后有一个额外的约束条件,二元变量的总和是&lt; = 1。

这个答案从John Foreman的 Data Smart 的第4章“优化建模”中大量解释。我强烈推荐它。