8
3

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 3 years have passed since last update.

完全栄養マクドナルド食問題を解いてみた

Last updated at Posted at 2021-03-07

概要

Pythonの数理最適化ライブラリであるPuLPを使用する練習として、多くの人がやっている完全栄養マクドナルド食問題(一日に必要な栄養素を全て取れる、マクドナルドの商品の組み合わせを探す問題)にチャレンジしてみました。

参考

参考にしたのは、以下の記事です。

問題設定

1日に必要な栄養素を取りつつ、塩分や脂肪は上限を超えないようにして、できるだけ低カロリーな食事になることを目指します。

1日に必要な栄養素の基準としては、以下のグリコのサイトを参考にしました。

年齢や性別で推奨量・目安量が違うので、今回は女性・30~49(歳)の値を基準値とします。また、脂質については総エネルギーに占める割合で目標量が決まりますが、今回は上限として45gを設定しました。以下が、今回の分析における栄養素の条件になります。

栄養名 下限 上限
タンパク質(g) 50.0
脂質(g) 45.0
カリウム(mg) 2000.0
カルシウム(mg) 650.0
リン(mg) 800.0 3000.0
鉄(mg) 10.5 40.0
ビタミンA(μg) 700.0 2700.0
ビタミンB1(mg) 1.1
ビタミンB2(mg) 1.2
ナイアシン(mg) 12.0 250.0
ビタミンC(mg) 100.0
食物繊維(g) 18.0
食塩相当量(g) 6.5

また、1回の食事で同じものを2つ食べることになるのは辛いので、同じ商品は最大3つまでとします。一応ですが、たくさん食べることもできないので、1日に食べる食事の合計の重さは2kg以下に設定します。まー、これは特に影響がないでしょう。

マクドナルドの商品の栄養価は、マクドナルドのwebページで公開されています。

以上の条件を反映した整数計画問題を作成して、PuLPを使いカロリーの低い組み合わせを探します。

結果

分析に使用したコードは最後に紹介するとして、分析の結果は以下のようになりました。

出力結果
個数_えだまめコーン = 3.0
個数_りんご&クリーム = 1.0
個数_ケチャップ = 1.0
個数_サイドサラダ = 3.0
個数_シャカチキ_チェダーチーズ味シーズニング = 1.0
個数_シャカチキ_レッドペッパー味シーズニング = 2.0
個数_プチパンケーキ = 1.0
個数_プレミアムローストコーヒー(S) = 3.0
個数_マックグリドル_ベーコンエッグ = 1.0
個数_マックシェイク_チョコレート(M) = 1.0
個数_マックシェイク_チョコレート(S) = 1.0
個数_野菜生活100(S) = 1.0

総カロリー:1604.0kcal

シャカチキ用のパウダーはあるが、シャカチキはないという悲しい結果。。。
総カロリーが約1600キロカロリーなので、ダイエット食としては悪くないですね。

とりあえず朝食、昼食、夕食に分けてみると、

朝食

  • プチパンケーキ
  • りんご&クリーム
  • えだまめコーン
  • シャカチキ_レッドペッパー味シーズニング
  • サイドサラダ
  • プレミアムローストコーヒー(S)
  • マックシェイク_チョコレート(S)

昼食

  • マックグリドル_ベーコンエッグ
  • ケチャップ
  • えだまめコーン
  • シャカチキ_チェダーチーズ味シーズニング
  • サイドサラダ
  • プレミアムローストコーヒー(S)
  • マックシェイク_チョコレート(M)

夕食

  • えだまめコーン
  • シャカチキ_レッドペッパー味シーズニング
  • サイドサラダ
  • プレミアムローストコーヒー(S)
  • 野菜生活100(S)

ダイエット中なので、夜は炭水化物を控えるということですね。
シャカチキ用のパウダーは、えだまめコーンあたりにかけて食べましょう。

摂取する栄養は以下の通りです。

出力結果
タンパク質:52.1
脂質:43.3
炭水化物:251.6
ナトリウム:2610.0
カリウム:2915.0
カルシウム:816.0
リン:1345.0
:10.5
ビタミンA:770.0
ビタミンB1:1.3
ビタミンB2:1.4
ナイアシン:12.0
ビタミンC:102.0
コレステロール:247.0
食物繊維:18.4
食塩相当量:6.5

コード

計算に使ったコードは以下になります。

分析コード
import pandas as pd
import numpy as np
import pulp

df = pd.read_csv("マクドナルド栄養価.csv",index_col=0)

# 欠損値を0埋め
df = df.fillna(0)

# カロリーが0の商品を除く
df = df[df['エネルギー']>0]

# 栄養値の上限・下限の入力
df_max_min = pd.read_csv("1日分の栄養.csv",index_col=0)

# 変数の設定
df["個数"] = pulp.LpVariable.dicts('個数',df.index,lowBound=0,upBound=3,cat='Integer').values()

# 最小化問題
problem = pulp.LpProblem('完全栄養マクドナルド問題',sense=pulp.LpMinimize)

# 目的変数の設定
problem.setObjective(pulp.lpDot(df["エネルギー"],df["個数"]))

# 拘束条件
for nutrition in df_max_min.index:
    if not np.isnan(df_max_min.loc[nutrition,"max"]):
      problem.addConstraint(pulp.lpDot(df[nutrition],df["個数"])<=df_max_min.loc[nutrition,"max"])
    if not np.isnan(df_max_min.loc[nutrition,"min"]):
      problem.addConstraint(pulp.lpDot(df[nutrition],df["個数"])>=df_max_min.loc[nutrition,"min"])

# 重量に条件
problem.addConstraint(pulp.lpDot(df["製品重量"],df["個数"])<=2000)

# 問題を解く
status = problem.solve()
print(pulp.LpStatus[problem.status])

# 答えの表示
for v in problem.variables():
  if v.varValue != 0:
    print(f'{v.name} = {v.varValue}')

print(f"総カロリー:{pulp.value(problem.objective)}kcal")

for nutrition in df_max_min.index:
  print(nutrition + ":" + str(round(pulp.lpDot(df[nutrition], df["個数"]).value(),ndigits=1)))
8
3
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
8
3

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?