みなさんは健康的な食事を摂れていますか。健康的な食事の基準として、1日の必要栄養摂取量を満たしているかがあります。しかし、そのためにはいろいろな食材を摂る必要があり、忙しい人にとってはなかなか実現が難しいですよね。
そこで今回は、数理最適化というものを使って、1日の栄養素を満たせる最低限の食材を算出してみます。
データ
1日の栄養素を満たす食品を算出するには、1日に必要な栄養素の量と、食材毎の栄養素のデータが必要になります。
1日の栄養摂取量
1日に必要な栄養素の量は、厚生労働省が定める「日本人の食事摂取基準」を使用します。
この表から以下のことが分かります。
- 性別と年齢によって、摂取量は異なる。
- 摂取量としては推定平均必要量・推奨量・目安量・目標量が存在する。
- ビタミンA(右図)のように耐用上限量が決まっている栄養素もある。
また、栄養素は全部で31種類存在します。
食材毎の栄養素
食材毎の栄養素は文部科学省が作成している「日本食品標準成分表」を使用します。
こちらには2478品目の食材の各栄養素の量が記載されています。
数理最適化
以上2つのデータを使って数理最適化により、1日の栄養素を満たせる最低限の食材を算出するわけですが、そもそも数理最適化とは何かを簡単に説明します。
数理最適化とは
数理最適化とは、「制約が与えられた中で、ある関数を最小化(または最大化)する解を見つけること」です。最小化したい関数のことを目的関数といいます。
言葉だけでは分かりにくいので、例を見てみましょう。
(例)
xが0から2の範囲で、関数y=x*2が最小値をとるxの値を見つけたいとします。
求めたい変数:x
制約:0<x<2
目的関数:y=x*2
これを解くと、解はx=1.115になります。
数理最適化の解き方のアルゴリズムとしては、シンプレックス法や分枝限定法等がありますが、脱線するので今回は割愛します。
栄養問題を数理最適化に当てはめる
では、今回の目的である「1日の栄養素を満たせる最低限の食材を算出する」を数理最適化に当てはめてみます。今回求めたいのは食材なので、各食材の摂取量をxとし、栄養素が1日の推奨量を超えているという条件のもと、食材の合計摂取量を最小化します。
求めたい変数:各食材の摂取量(g)
制約式:ある栄養素の摂取量(g)>1日の推奨量(g)
目的関数:食材の合計摂取量(g)
数式で表すと以下になります。
実装
pythonの数理最適化ライブラリであるpulpを使って実装します。
必要摂取量は性別や年齢によって異なるので、ひとまずは20代男性の数値で算出します。
from pulp import * problem = LpProblem(sense=LpMinimize) P = df.index.to_list() x = LpVariable.dicts('x', P, cat='Continuous', lowBound=0, upBound=200) for t in target['栄養素'].to_list(): if target.loc[target['栄養素']==t,'以上以下'].values[0] == '以上': problem += lpSum([df.loc[p,t]*x[p] for p in P])>=target.loc[target['栄養素']==t,'男20上限'].values[0] if target.loc[target['栄養素']==t,'以上以下'].values[0] == '以下': problem += lpSum([df.loc[p,t]*x[p] for p in P])<=target.loc[target['栄養素']==t,'男20上限'].values[0] if target.loc[target['栄養素']==t,'男20上限'].values[0] != 0: problem += lpSum([df.loc[p,t]*x[p] for p in P])<=target.loc[target['栄養素']==t,'男20上限'].values[0] problem += lpSum([1*x[p] for p in P]) status = problem.solve() pulp.value(problem.objective)
LpVariable.dicts('x', P, cat='Continuous', lowBound=0, upBound=200)
のlowBoundとupBoundで変数xの下限と上限を設定しています。摂取量がマイナスになることはないので下限は0を、1つの食材を大量に摂取することは避けたいため上限は200gを設定しています。
栄養素に関して上限量が存在するものは、上限量を越えないように制約式を追加しています。
結果
結果①
上記コードを実行して、結果を確認してみます。
いろいろと出力されていますが、大事なのは赤枠の部分になります。「Optimal」というのは最適解が見つかったという意味です。制約式によっては、「解なし」となる場合もあります。
次の「objective value」というのは最適解の時の目的関数の値になります。565グラム分の食材を摂取すれば、必要な栄養素が満たせるようです。
ではどの食材がそれぞれ何グラム選ばれたのかを見てみましょう。
食品 | 摂取量(g) |
---|---|
(砂糖類) 加工糖 角砂糖 | 200.0 |
(植物油脂類) ひまわり油 ハイリノール | 60.3 |
りょくとう 全粒 乾 | 58.8 |
ごま いり | 46.6 |
らっかせい 大粒種 いり | 39.8 |
まいたけ 乾 | 39.6 |
だいず [全粒・全粒製品] きな粉 黄大豆 全粒大豆 | 34.3 |
(砂糖類) 加工糖 氷砂糖 | 29.4 |
アーモンド いり 無塩 | 12.9 |
<茶類> (緑茶類) せん茶 茶 | 12.0 |
レンズまめ 全粒 乾 | 7.8 |
(動物油脂類) たらのあぶら | 6.8 |
こめ [その他] 米ぬか | 5.0 |
<畜肉類> うし [副生物] 肝臓 生 | 4.5 |
アセロラ 酸味種 生 | 3.9 |
らっかせい 大粒種 乾 | 2.5 |
<調味料類> (だし類) 昆布だし 煮出し | 0.9 |
...角砂糖200g?...ひまわり油60g?
こんなの食べたら死んでしまいます。
摂取量を最小化したため、少ない量でカロリー摂取量を一気に稼げる角砂糖が選ばれたのだと思います。
いくら栄養素を満たしていても、現実的に食べれない量では困ります。
修正
ではどのように修正すれば良いでしょうか?
各食材の摂取量上限を200gとしていたので、これを20gとかに減らすべきでしょうか?そうすれば角砂糖が200gも選ばれることはなくなりますが、一方で200gぐらい余裕で食べれる食材(ご飯等)も20gまでしか食べれない制約をかけてしまうことになります。
また、1つの食材の上限量を減らすことは、必要な食材数を増やすことになるので、今回の目的である最低限の食材という部分が満たされません。
食材毎に現実的に食べれる量のデータがあればいいのですが、残念ながらそのようなデータは見つかりませんでした。
今回は制約はこのままで、実行結果から食べれない量の食材が含まれていたら、その食材自体を手動で除外することにします。食べれない量の食材がなくなるまで、除外して実行してを繰り返します。
結果、以下のワードが含まれる食品を除外しました。
'植物油脂類|動物油脂類|香辛料類|節|煮干し|カゼイン|たたみいわし|茶|酵母|さめ類|てんぐさ|めふん|粉乳類|グァバ|加工品|粉末|精粉|からすみ|くさや|はと|とうがらし|ホエー|うるか|パセリ|マジェランあいなめ|あらげきくらげ|ドライトマト|あまに|心臓|調味料類|ココア|いなご|フォアグラ|ごま|らっきょう|かや|ゆず|すいか|おおむぎ|かぼちゃ いり|すだち|ライむぎ|つるにんじん|どじょう|あずき|ココナッツ|凍みこんにゃく|テンペ|きな粉|やぶまめ|ひまわり|なずな|ささげ|あめ煮|らいまめ|アメリカほどいも|おから|そば粉|ショートニング|マーガリン|ケーキ|でん粉|砂糖類|和干菓子類|アルコール飲料類|あわもち|ぎんなん 生|セレベス|キャンデー類|まつ 生|乳児用|コーングリッツ|コーンフラワー|和生菓子|メープルシロップ|ガム|たにし|ドリアン|あけび|アーモンド 乾|アーモンド フライ 味付け|素干し|大豆はいが|りょくとう|黄大豆|2等|けし'
- 調味料系
- マイナーな食材:たたみいわし、めふん
- 高級食材:フォアグラ
- 食べたくない食材:くさや、ドリアン、乳児用食材
結果②
上記の食品を除外すると、以下の結果となりました。
これが最強の食事です!
食品 | 摂取量 (g) |
---|---|
こむぎ [小麦粉] 薄力粉 1等 | 200.00 |
いんげんまめ 全粒 乾 | 200.00 |
レンズまめ 全粒 乾 | 185.68 |
モロヘイヤ 茎葉 生 | 72.76 |
まいたけ 油いため | 67.88 |
らっかせい 小粒種 いり | 48.67 |
マンゴー ドライマンゴー | 36.04 |
アーモンド いり 無塩 | 30.00 |
まいたけ 乾 | 16.37 |
なつめやし 乾 | 15.63 |
チアシード 乾 | 10.00 |
<畜肉類> うし [副生物] 肝臓 生 | 4.53 |
アセロラ 酸味種 生 | 1.44 |
(こんぶ類) 刻み昆布 | 0.05 |
※アーモンドとチアシードのみ上限をそれぞれ30g,10gとしています。
この食事による各栄養素の摂取量は以下の通りです。
栄養素 | 必要摂取量 | 上限量 | この食事での摂取量 |
---|---|---|---|
カロリー | 2650.0 | - | 2650.0 |
タンパク質 | 65.0 | - | 136.0 |
脂質 | 58.9 | - | 58.9 |
炭水化物 | 331.3 | - | 457.4 |
食物繊維 | 21.0 | - | 103.3 |
ビタミンA | 850.0 | 2700.0 | 850.0 |
ビタミンD | 8.5 | 100.0 | 8.5 |
ビタミンE | 6.0 | 850.0 | 24.2 |
ビタミンK | 150.0 | - | 523.0 |
ビタミンB1 | 1.4 | - | 3.2 |
ビタミンB2 | 1.6 | - | 2.1 |
ビタミンB6 | 1.4 | 55.0 | 2.7 |
ビタミンB12 | 2.4 | - | 2.4 |
ナイアシン | 15.0 | 300.0 | 37.8 |
葉酸 | 240.0 | 900.0 | 784.7 |
パントテン酸 | 5.0 | - | 9.6 |
ビオチン | 50.0 | - | 121.3 |
ビタミンC | 100.0 | - | 100.0 |
ナトリウム | 7.5 | - | 7.5 |
カリウム | 2500.0 | - | 7050.3 |
カルシウム | 800.0 | 2500.0 | 800.0 |
マグネシウム | 340.0 | - | 825.9 |
リン | 1000.0 | 3000.0 | 2371.3 |
鉄 | 7.5 | 50.0 | 34.0 |
亜鉛 | 11.0 | 40.0 | 20.2 |
銅 | 0.9 | 7.0 | 5.4 |
マンガン | 4.0 | 11.0 | 10.2 |
ヨウ素 | 130.0 | 3000.0 | 130.0 |
セレン | 30.0 | 450.0 | 117.4 |
クロム | 10.0 | 500.0 | 16.7 |
モリブデン | 30.0 | 600.0 | 600.0 |
当然ですが全ての必要摂取量を満たしており、上限量がある栄養素は上限量を越えないようになっています。多くの栄養素は必要摂取量に近い量を摂取していますが、葉酸・カリウム・モリブデンなど必要以上に摂取している栄養素も見られます。