
Пусть у нас есть картофель фри, котлета, хлеб, помидор, огурец и молочный коктейль. Сколько чего нужно съесть, чтобы получилось 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 гр.
