Пусть у нас есть картофель фри, котлета, хлеб, помидор, огурец и молочный коктейль. Сколько чего нужно съесть, чтобы получилось 30 гр. белка, 25 гр. жиров и 60 гр. углеводов? В прошлый раз я баловался и пытался решить это с помощью матриц, на этот раз - с помощью линейных уравнений и python библиотеки PuLP.
Немного про PuLP (Python Linear programming)
Пользоваться этим модулем очень просто.
Например, если нам нужно создать переменную 0 <= x <= 3, то синтаксис будет такой:
x = LpVariable("x", 0, 3)
Для переменной 0 <= y <= 1:
y = LpVariable("y", 0, 1)
Функция LpProblem() для создания задачи:
prob = LpProblem("Моя проблемка", LpMinimize)
Дальше можно комбинировать переменные, выражения и константы, добавляя их в задачу:
prob += x + y <= 2
Если вы добавите выражение (не ограничение), оно станет целью: (в оригинале If you add an expression (not a constraint), it will become the objective:)
prob += -4*x + y
Для решения можно использовать встроенный "решатель":
status = prob.solve()
Просмотр статуса у решения:
LpStatus[status]
> 'Optimal'
И можно после этого получить значение:
value(x)
> 2.0
Полная документация https://coin-or.github.io/pulp/.
Решение с помощью PuLP полный код
Вот такие у нас есть продукты с такими белками, жирами и углеводами ( БЖУ) на 100 гр. продукта:
Белки | Жиры | Углеводы | |
Картофель фри | 3,4 | 15 | 41 |
Котлета | 14,1 | 15,7 | 6,6 |
Хлеб | 8,8 | 3,3 | 46,7 |
Помидор | 1,1 | 0,2 | 3,8 |
Огурец | 0,8 | 0,1 | 2,5 |
Молочный коктейль | 6 | 5 | 45 |
Ставим:
pip install pulp
Полный код программы, можно вставить в файл и запустить:
import pulp as pl
data = {
'Картошка фри': {'Б': 3.4, 'Ж': 15, 'У': 41},
'Котлета': {'Б': 14.1, 'Ж': 15.7, 'У': 6.6},
'Хлеб': {'Б': 8.8, 'Ж': 3.3, 'У': 46.7},
'Помидор': {'Б': 1.1, 'Ж': 0.2, 'У': 3.8},
'Молочный коктейль': {'Б': 6, 'Ж': 5, 'У': 45},
'Огурец': {'Б': 0.8, 'Ж': 0.1, 'У': 2.5},
}
# Макронутриенты (БЖУ), которые нужно получить
needed = {'Б': 30, 'Ж': 25, 'У': 60}
# Минимум 10 гр. надо съесть, иначе может посчитать с нулями
# Можно задать для каждого продукта отдельно
MIN = 0.1
# Максимум 150 гр.
MAX = 1.5
prob = pl.LpProblem("The Nutrients Problem", pl.LpMinimize)
# Создаем переменные для каждого продукта
food = pl.LpVariable.dicts('Food', data, MIN, MAX)
for nutrient in list('БЖУ'):
# Для бел., жиров и угл. создаем условия
prob += pl.lpSum([data[k][nutrient] * food[k]
for k in data]) == needed[nutrient]
prob.writeLP("Nutrients.lp")
prob.solve()
# Нельзя съесть -минус 10 гр. огурцов. Значит решения нет, если есть хоть 1
# отрицательное значение
if any((v.varValue or 0) < 0 for v in prob.variables()) is False:
for v in prob.variables():
if not v.varValue:
continue
name = v.name.replace('Food_', '')
weight = int(v.varValue*100)
print(f'{name}: {weight} гр.')
В коде есть такая строка (она не обязательна):
prob.writeLP("Nutrients.lp")
Она создает файл Nutrients.pl, если заглянуть внутрь него, то можно увидеть все формулы, константы и условия, что очень удобно:
\* The_Nutrients_Problem *\
Minimize
OBJ: __dummy
Subject To
_C1: 3.4 Food_Картошка_фри + 14.1 Food_Котлета + 6 Food_Молочный_коктейль
+ 0.8 Food_Огурец + 1.1 Food_Помидор + 8.8 Food_Хлеб = 30
_C2: 15 Food_Картошка_фри + 15.7 Food_Котлета + 5 Food_Молочный_коктейль
+ 0.1 Food_Огурец + 0.2 Food_Помидор + 3.3 Food_Хлеб = 25
_C3: 41 Food_Картошка_фри + 6.6 Food_Котлета + 45 Food_Молочный_коктейль
+ 2.5 Food_Огурец + 3.8 Food_Помидор + 46.7 Food_Хлеб = 60
Bounds
0.1 <= Food_Картошка_фри <= 1.5
0.1 <= Food_Котлета <= 1.5
0.1 <= Food_Молочный_коктейль <= 1.5
0.1 <= Food_Огурец <= 1.5
0.1 <= Food_Помидор <= 1.5
0.1 <= Food_Хлеб <= 1.5
__dummy = 0
End
Отрицательные значения
Есть байка, что древние римляне ходили с гусиным перышком на пир, чтобы в нужный момент пощекотать себе горло, очистить желудок и наслаждаться пиршеством дальше. Но даже если бы Миа была бы древней римлянкой, и ходила с пером, то это бы ей не помогло, когда программа выдаст съесть - минус 100 грамм картошки фри. Поэтому я отбросил в коде такие вариации.
Код на гитхаб
Итого
Если бы Миа пришла в ресторан со своими весами, а не с запрещенными веществами, и с ясной целью получить 30 гр. белка, 25 гр. жиров и 60 гр. углеводов, то она должна была бы съесть:
Картошка фри: 10 гр.
Котлета: 128 гр.
Молочный коктейль: 10 гр. (
После Винсента не стоит его пить)Огурец: 150 гр.
Помидор: 150 гр.
Хлеб: 71 гр.