Пусть у нас есть картофель фри, котлета, хлеб, помидор, огурец и молочный коктейль. Сколько чего нужно съесть, чтобы получилось 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 гр.
Комментарии (15)
Markscheider
20.09.2021 16:18А вы учитываете этот параметр в своем рационе или хотели бы его учитывать? Почему именно количество пищевых волокон, а не витамин А, например?
Учитываю клетчатку. Почему не витамины? Я считаю, что:
1) Если питаться разнообразно и полноценно (в соответствии с рекомендациями, ВОЗ, например) - все необходимые витамины приходят с едой. Исключение - витамин Д, но с ним все немного сложнее.
2) Если я даже увижу, что в рационе, например, не хватает витамина А - я не буду покупать его в аптеке, т.к. твердо убежден, что поливитамины (как и другие лекарства) должен разначать врач. Лучше - по ДМС :):):)
А с клетчаткой дело другое: передознуться ей сложно (хотя можно), а вот недостаток ее при нашей рафинированной еде случается часто. Плюсуем малоподвижный образ жизни и получаем, как минимум, систематические запоры. Далее со всеми остановками.
Вот поэтому я хотел бы помимо БЖУ контролить еще и пищевые волокна. Это, программа минимум, ессн.
Буду рад уточнениям и разъяснениям от профессиональных медиков, если в моем комментарии что-то не так.
P.S. Ожидаемо промахнулся веткой - не привык к новому дизайну Хабра :)
pcdesign Автор
20.09.2021 16:26Благодарю за развернутый ответ. Да, логика понятна. Клетчатка нужна, особенно при большом потреблении белка. Я пока только БЖУ считаю. Но на счет клетчатки задумывался тоже. А какие нормы на пищевые волокна? Вот сколько рекомендуется, женщине средних лет с именем Миа, потреблять пищевых волокон в день?
Все же у меня не калькулятор, а подгонялка. Программа пытается именно подогнать исходя из заданных значений.
Markscheider
20.09.2021 16:39ВОЗ в рекомендациях для населения говорит о 400 гр овощей в сутки (пруф). Правда, мне, почему-то кажется, что картошка-фри в данном случае к овощам относиться не будет :):)
А именно по чистой клетчатке (ее, как вы понимаете, в разных фруктах/овощах по-разному содержится) советуют взрослым по 25-30 грамм в день (пруф).Но надо понимать, что клетчатка есть еще и в зерновых продуктах, бобовых и пр. И, к сожалению, ее содержание (наряду с БЖУ) указывают не на всех магазинных продуктах.
P.S. У меня есть ощущение, что в бургере с белой булочкой из муки высшего сорта и картошке-фри (очищенной, а не не "деревенской" со шкуркой) этой клетчатки содержится ноль целых, хрен десятых...
pcdesign Автор
20.09.2021 17:28Понятно, спасибо.
Подгонялка выдала для нее 150 гр. помидор и 150 гр. огурцов, так что в принципе за один прием пищи 3/4 нормы она получит.
Markscheider
20.09.2021 17:58Чуток еще добавлю: не про программирование, а про еду :). Из собственных наблюдений.
400 грамм свежих фруктов и овощей в день поначалу кажутся какой-то огромной и жуткой цифрой. Однако съедать это количество вполне реально. Главное - не увлекаться монодиетой (один вид овощей/фруктов). Это и скучно, да и с точки зрения разнообразия нутриентов неверно.
Ну и в один прием пищи все 400 грамм упихивать не стоит - будет тяжеловато в желудке за счет объема.
К зиме будет посложнее, а сейчас, по осени, и вовсе ненапряжно: настрогал на обед салатик из помидора с огурцом и болгарским перцем, на полдник рубанул подмосковное яблочко, а перед ужином зашла соседка и принесла кабачков (о, это слово!) со своего огорода...
pcdesign Автор
20.09.2021 18:05Да, раньше все думали, что достаточно просто считать калории. Потом пришли к тому, что нужно считать БЖУ. Видимо следующим шагом будет контроль над нормативами по клетчатке.
Dolios
21.09.2021 09:52400 грамм свежих фруктов и овощей в день поначалу кажутся какой-то огромной и жуткой цифрой.
Кому кажутся? Это 2 яблока или 1 салат из свежих овощей.
pcdesign Автор
21.09.2021 10:03Возможно, тому у кого нет кухонных весов или тот кто не знает сколько весит среднее яблоко.
Markscheider
21.09.2021 11:03Вот про весы - жизненно. Лет пять назад купил электронные и теперь не понимаю - как я до этого без них обходился. Причем речь не только про учет калорий - это вообще в быту вещь незаменимая. Даже ингредиенты для алкогольных коктейлей :) удобнее отмерять на весах, нежели морочиться с градуированными стаканчиками.
На фото - овощи c ≈300 гр чистого веса. К слову, салат из них получится довольно мощный, если по столовским меркам - так на две порции...Фото
VPryadchenko
20.09.2021 17:46Если вы добавите выражение (не константу), оно станет целью: (в оригинале If you add an expression (not a constraint), it will become the objective:)
constraint - это не константа, это - ограничение.
Markscheider
Добавляйте в качестве одного из параметров "количество пищевых волокон", лепите приложение и выкатывайте в прод.
Калькулятор будет полезен для тех, кто следит за рационом "вполглаза" и не хочет ставить более функциональный комбайн типа FatSecret.
А в платной версии можно добавить такой же параметрический расчет по разным витаминам, простым углеводам (сахарам) и пр. С добычей исходных данных по содержанию того или иного компонента в продукте/блюде, правда, придется повозиться.
pcdesign Автор
Спасибо. А я что-то мыслю категориями для дома и для семьи, и не задумывался о таком использовании ))
Параметр, да, добавить не сложно, в базе usda есть эти данные, я про нее писал в прошлой статье. А вы учитываете этот параметр в своем рационе или хотели бы его учитывать? Почему именно количество пищевых волокон, а не витамин А, например?