Формулировка минимизации LP целлюлозы Выберите один тип ограничения

Приведенный ниже код используется для запуска задачи минимизации 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))

Проблема, с которой я столкнулся, заключается в создании такого ограничения. Я думал использовать двоичную переменную, но не знаю, как это реализовать. Любая помощь будет оценена


person samuel oyeleye    schedule 04.05.2017    source источник


Ответы (1)


Что вам нужно сделать, так это иметь двоичные переменные для каждого выбранного типа еды, а затем ограничение, что их сумма равна 1, что означает, что в точности одна из двоичных переменных равна 1, а остальные - 0.

Проблема в том, что включение, например, бинарной переменной для завтрака означает, что в вашей линейной программе есть условие «если-то». Если выбрано хотя бы одно блюдо для завтрака, тогда оно будет равно 1, иначе 0. Оператор «если-то» не является линейным, поэтому нам нужен умный способ сделать это линейным. Мы можем сделать это с помощью «большого ограничения M».

Сделайте переменные Python, представляющие сумму решений для каждого типа еды, например breakfast_sum, lunch_sum и т. Д.

Затем создайте двоичные переменные PuLP breakfast_binary, lunch_binary и т. Д.

Мы будем использовать большое ограничение M, чтобы breakfast_binary "переворачивался", когда breakfast_sum больше 0. Затем мы будем использовать другое ограничение, чтобы убедиться, что сумма двоичных переменных ‹= 1.

М - в основном большое число. Насколько он должен быть большим? Обратите внимание, что вы никогда не будете выделять более 2 порций каждого элемента для завтрака, поэтому попробуйте сделать M = 2 * {количество элементов для завтрака}. Теперь посмотрим на это ограничение:

M * breakfast_binary >= breakfast_sum.

Если breakfast_sum равно 0, тогда breakfast_binary разрешено равным 0. Как только вы выделяете порцию элемента для завтрака, breakfast_binary принудительно переключается на 1.

Сделайте это с обедом, ужином и т. Д., А затем поставьте дополнительное ограничение, заключающееся в том, что сумма двоичных переменных ‹= 1.

Этот ответ в значительной степени перефразирует из главы 4 «Оптимизационное моделирование» книги Data Smart Джона Формана. Я очень рекомендую это.

person thomaskeefe    schedule 09.05.2017