食事の最適化:最小限の食材で1日の必要栄養素を満たすには

みなさんは健康的な食事を摂れていますか。健康的な食事の基準として、1日の必要栄養摂取量を満たしているかがあります。しかし、そのためにはいろいろな食材を摂る必要があり、忙しい人にとってはなかなか実現が難しいですよね。

そこで今回は、数理最適化というものを使って、1日の栄養素を満たせる最低限の食材を算出してみます。

データ

1日の栄養素を満たす食品を算出するには、1日に必要な栄養素の量と、食材毎の栄養素のデータが必要になります。

 

1日の栄養摂取量

1日に必要な栄養素の量は、厚生労働省が定める「日本人の食事摂取基準」を使用します。

www.mhlw.go.jp

この表から以下のことが分かります。

  • 性別と年齢によって、摂取量は異なる。
  • 摂取量としては推定平均必要量・推奨量・目安量・目標量が存在する。
  • ビタミンA(右図)のように耐用上限量が決まっている栄養素もある。

また、栄養素は全部で31種類存在します。

食材毎の栄養素

食材毎の栄養素は文部科学省が作成している「日本食品標準成分表」を使用します。

www.mext.go.jp

こちらには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

当然ですが全ての必要摂取量を満たしており、上限量がある栄養素は上限量を越えないようになっています。多くの栄養素は必要摂取量に近い量を摂取していますが、葉酸カリウムモリブデンなど必要以上に摂取している栄養素も見られます。