Как стать автором
Обновить

PULP БЖУ

Время на прочтение3 мин
Количество просмотров6.2K

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

Теги:
Хабы:
Всего голосов 4: ↑3 и ↓1+2
Комментарии15

Публикации

Истории

Работа

Python разработчик
119 вакансий
Data Scientist
78 вакансий

Ближайшие события

7 – 8 ноября
Конференция byteoilgas_conf 2024
МоскваОнлайн
7 – 8 ноября
Конференция «Матемаркетинг»
МоскваОнлайн
15 – 16 ноября
IT-конференция Merge Skolkovo
Москва
22 – 24 ноября
Хакатон «AgroCode Hack Genetics'24»
Онлайн
28 ноября
Конференция «TechRec: ITHR CAMPUS»
МоскваОнлайн
25 – 26 апреля
IT-конференция Merge Tatarstan 2025
Казань