【神回】スターバックスのメニューだけで1日分の栄養は摂取できるのか?コンピューターに計算させてみた.

スポンサーリンク

今回は元ネタがありまして,それがこちらの記事↓

まずアイデアがすごい.そしてそれを成し遂げる知識量もすごい.

今回はこのアイデアを真似させてもらって(ネタパクと言われても否定はできない),みなさん大好きスターバックスのメニューだけで1日分の栄養を摂取できるのかを計算したいと思います.

下準備

お勉強から始める

元ネタにも説明はありますが,こういった問題は「ダイエット問題」と呼ばれているようです.(初耳)

まずは「ダイエット問題」について簡単な例題を交えて解説してくれているサイトがあるのでこれを見てお勉強しました.

PuLPで線形計画問題を解く - 備忘録置いとく
最近PuLPという線形計画問題 (や混合整数問題) を解くためのPythonライブラリを触ったのでそれの使い方をメモしておきます。まず線形計画問題について簡単に説明をします。線形計画問題とは用語の復習から。実数$c_1, \dots, c_n \in \mathbb{R}$に対して、次のように与えられる実変数$x...

ふむふむ,なんとなく理解できた.

一応ここでも例をあげて説明すると,↓のような成分表があったとして,

ドリンクAドリンクBドリンクC
タンパク質 1[g]タンパク質 2[g]タンパク質 3[g]
カロリー 80[kcal]カロリー 150[kcal]カロリー 180[kcal]

タンパク質15[g]以上を摂取でき,総カロリーを最小化させられるようなドリンクの組み合わせを求めたいとします.

ドリンクAの個数をXa,ドリンクBの個数をXb,ドリンクCの個数をXcとしてカロリーを最小化させるための式はこのようになります.

最小化: Xa×80+Xb×150+Xc×180

タンパク質の制約条件を式で表すと,

Xa×1+Xb×2×Xc×3≧15

という式になって,これらの条件を満たすようなドリンクの組み合わせをコンピューターに解かせようぜ!っていうかんじです.

変数(ここでは栄養成分)の数が増えるとそれだけ解を求めるのが大変になり,人間ではとうてい解けない計算量になってしまうのでコンピューターにお任せします.

公開されているスタバのメニューの成分表

スタバのドリンクとフードの栄養成分が公開されているこのデータを参照します.

ドリンクの栄養成分情報

フードの栄養成分情報

この栄養成分表を見るとわかりますが,カロリーとPFC(タンパク質,脂質,炭水化物),食塩相当量しか載っていないので,これらの栄養成分のみを求めることにします.(元ネタではカルシウムやビタミンなど細かく求めていましたが今回は許して)

期間限定のドリンクは除外し,ミルクの種類も低脂肪タイプや豆乳などありますが今回はミルクのみにします.アイスとホットでは地味に成分が違うみたいなのでこれはちゃんと考慮します.サイズはTallのみにします(キッズ用ココアなどといった子供用はShortしかないなのでこれは例外)

中には成分0のメニュー(ホットティー数種類)があり,これは計算上不都合が生じそうなので全ての成分が0のメニューも除外させてもらいます.(そもそもカロリー0なので意味がない)

ということで,ドリンク64種,トッピング14種,フード48種の組み合わせが考えられることになりました(多い)

実験環境

プログラムの実行環境はGoogle Colaboratory(ブラウザ上でPythonが実行できるサービス)で,言語はPythonを使います.

今回作成したプログラムはこちらから見ることができ,上から順に実行してもらえればそのまま動くと思います(多分)

今回の条件

男性(18~29歳)の1日あたりの食事摂取基準量を目標値にします.(参考元)

エネルギー2650[kcal]
たんぱく質65[g]
脂質73[g]※1
炭水化物397.5[g]※2
ナトリウム(食塩相当量)7.5[g]未満

※1 エネルギーの25%分とし,1[g]/9[kcal]として算出

※2 エネルギーの60%分とし,1[g]/4[kcal]として算出

念の為に言っておくと,今回は「それぞれの栄養成分を最低限摂取できて,目標カロリー以内(最長値)になるような組合わせを導き出そう!」という目標になります.

プログラムをひたすら書いていく

準備は整ったので,後はプログラムを書いていくのみ.

私の理解力では元ネタのプログラムが理解できなかったので,さっき紹介したこの解説記事にのっているものを参考にしてプログラムを書いていきました.

今回のコードはそれぞれの栄養数値を手入力していかないといけないのでめちゃくちゃ大変でした.(全メニュー126種×栄養素5種の数値を手打ち)

まあ今回はひたすら栄養成分を入力するだけで,試行錯誤するところはなかったので3時間ほどで書き終わりました.

いちおう私が書いたプログラムを説明しますが興味ない人は飛ばしてくださいね.

import pulp

これは今回使う線形計画問題を解くためのPythonのライブラリをインポートしています.

optimizer = ['coffee1','coffee2','coffee3','coffee4','coffee5','coffee6','coffee7','coffee8','coffee9','coffee10','coffee11','coffee12',
             'espre1','espre2','espre3','espre4','espre5','espre6','espre7','espre8','espre9','espre10','espre11','espre12','espre13','espre14','espre15','espre16','espre17','espre18','espre19','espre20','espre21',
             'frapp1','frapp2','frapp3','frapp4','frapp5','frapp6','frapp7','frapp8',
             'sonota1','sonota2','sonota3','sonota4','sonota5','sonota6','sonota7','sonota8','sonota9','sonota10','sonota11','sonota12','sonota13',
             'tealat1','tealat2','tealat3','tealat4','tealat5','tealat6','tealat7','tealat8','tealat9',
             'hottea1',
             'custom1','custom2','custom3','custom4','custom5','custom6','custom7','custom8','custom9','custom10','custom11','custom12','custom13','custom14',
             'pastry1','pastry2','pastry3','pastry4','pastry5','pastry6','pastry7','pastry8','pastry9','pastry10','pastry11','pastry12','pastry13','pastry14',
             'sand1','sand2','sand3','sand4','sand5','sand6','sand7','sand8','sand9','sand10','sand11',
             'desert1','desert2','desert3','desert4','desert5','desert6','desert7','desert8','desert9','desert10','desert11','desert12','desert13',
             'package1','package2','package3','package4','package5','package6','package7','package8','package9','package10'
             ]

この中身にそれぞれメニュー名(自分がつけたもの)を書いて,「この中の組み合わせで解いてください!」という記述です.

calory = {
    'coffee1':18,
    'coffee2':10,
    'coffee3':11,
    'coffee4':6,
    'coffee5':10,
    'coffee6':18,
    'coffee7':40,
    'coffee8':9,
    'coffee9':155,
    'coffee10':17,
    #~以下省略~

次にそれぞれの栄養成分の大枠を宣言して,中にそのメニューの栄養成分の数値を全て書いています.(かなり長いので省略して貼り付けてます)

ちなみに,それぞれメニュー名をそのまま書くのは本当に大変なので,例えばコーヒーであれば,’coffee1’,’coffee2’…のように上から順に番号付けして書いています.

あとは同じようにすべての栄養素分を書き出します(ここが一番大変)

prob = pulp.LpProblem('Staba Problem',pulp.LpMinimize)

optimizer_vers = pulp.LpVariable.dicts('Optimizer', optimizer, lowBound=0)

ここでは,先程の組み合わせを”最大化するのか””最小化するのか”を指定しています.今回は最小化させたいのでpulp.LpMinimizeですね.

下の1文がなにをしているのかはよくわかりませんでした!!!

prob += pulp.lpSum([calory[i] * optimizer_vers[i] for i in optimizer]), '総カロリー量'

ここでは,最小化させたい成分を指定しています.今回は最小のカロリーになる組み合わせを求めたいので,カロリーを指定しています.

#成約条件
prob += pulp.lpSum([calory[i] * optimizer_vers[i] for i in optimizer])>=2650, '総カロリー量'
prob += pulp.lpSum([protein[i] * optimizer_vers[i] for i in optimizer]) >=65, 'タンパク質量の最低量'
prob += pulp.lpSum([fat[i] * optimizer_vers[i] for i in optimizer]) >=73, '脂質量の最低量'
prob += pulp.lpSum([carbo[i] * optimizer_vers[i] for i in optimizer]) >=397.5, '炭水化物量の最低量'
prob += pulp.lpSum([salt[i] * optimizer_vers[i] for i in optimizer]) <=7.5, '塩分の最大量'

次に,制約条件について書いています.不等号があるのでわかりやすいと思いますが,ここでそれぞれの栄養成分の最低摂取量を指定しています.

#計算
prob.solve()

そして,たったこの1文で計算を実行させます!!(なんかカッコいい)

for v in prob.variables():
    print(f'{v.name} = {v.varValue}')

ここは計算とは関係なく,ただ実行結果を出力するためのプログラムです.

print(f'総カロリー数: {pulp.value(prob.objective)}kcal')

ここも結果を表示しているだけ.これは最終的に得られた組み合わせの総カロリーを表示させています.

結果発表

それでは,スターバックスのメニューだけで1日分の栄養を補えるメニューの組み合わせを発表いたします.

1品目 ホイップクリーム 0.6(トールサイズにのせる分を1.0としたときの量)

お,後に出てくるドリンクにトッピングできそう!!

2品目 キャラメルソース 114.4(トールサイズにのせる量を1.0としたとき)

は??????

これでキャラメルソースドリンクをつくれということだろうか.

3品目 ホットティー(ほうじ茶) 215.8杯

以上

え,終わり?

各栄養成分の結果はこうなりました.

目標値実験結果
エネルギー2650[kcal]2650[kcal]
たんぱく質65[g]65[g]
脂質73[g]117.6[g]
炭水化物397.5[g]397.5[g]
ナトリウム(食塩相当量)7.5[g]未満0[g]

考察

実験結果の脂質量を見てもらうとわかりますが,どうやらカロリーを目標値に近づけるためには,目標よりもかなり多めの脂質を取らなければならないようです

あと食塩が0[g]になっているのでこれは単純に設定が足りていませんでしたね...(制約の7.5[g]未満はちゃんと守ってるけどね)

とはいえ,今回の実験結果からほうじ茶がスタバのメニューの中で一番バランスのとれたチート級の飲み物であるという事実が判明しました.

次回,リベンジ

そもそも1店舗に215杯分のほうじ茶の在庫があるわけないので,まだ実用的な組み合わせではありません.(たぶんキャラメルソースも無理)

コンピューター君は計算は得意ですが,人間がほうじ茶を215.8杯も飲めないということがわかりません

コンピューター
コンピューター

え,人間ほうじ茶215杯も飲めんの?

ざっこwwwww

もちろんこのままでは終われないので,プログラムに改良を重ねて再挑戦します!

おれ任の次回作にご期待ください.

リベンジ回はこちら↓

タイトルとURLをコピーしました