食事の最適化:最小限の食材で1日の必要栄養素を満たすには
- 食事
- 数理最適化
- Python
この記事で分かること
- 1日に必要な栄養素を満たす食事を、数学で作れるのか
- 数理最適化すると、どんな食材が選ばれるのか
- 「栄養を満たす」と「現実的に食べられる」は両立するのか
1.「最強の食事」を数学で作ってみたい
毎日、何を食べるか考えるのは意外と面倒です。
しかも、
- タンパク質は足りているかな?
- 野菜はちゃんと食べたかな?
- ビタミンやミネラルは?
と考え始めると、なかなか大変です。
そこで、ふと思いました。
1日に必要な栄養素を全部満たしながら、できるだけ少ない量の食材で済む食事を、数学で作ることはできないだろうか?
今回はこれを、数理最適化を使って計算してみます。条件は 20 代男性としました。
2.まずはデータを集める
今回必要なのは、大きく 2 種類のデータです。
① 1日に必要な栄養素
厚生労働省の「日本人の食事摂取基準」を使います。ここから、20 代男性が 1 日に必要な栄養素の量を設定します。
日本人の食事摂取基準|厚生労働省健康の保持・増進のために参照する、エネルギーと栄養素の摂取量の基準。年齢・性別ごとに推奨量や耐容上限量が定められています。mhlw.go.jpmhlw.go.jp
栄養素によっては、「これ以上摂ってはいけない」という上限も決められています。ビタミン A がその例です。

② 食材ごとの栄養素
次に必要なのは、「この食材を 100g 食べると、どれくらいの栄養素を摂れるのか?」というデータです。
そこで、文部科学省の「日本食品標準成分表2020年版(八訂)」を使います。2,478 品目の栄養素が収録されています。
日本食品標準成分表・資源に関する取組|文部科学省食品に含まれる栄養成分の基礎的データ集。戦後から70年以上にわたって改訂・公表されており、栄養指導などに利用されています。mext.go.jpmext.go.jp
3.では、数学に「最強の食事」を作ってもらおう
ここで使うのが数理最適化です。
難しそうな名前ですが、考え方はシンプルです。
「これだけは守ってね」という条件の中で、一番いい答えを探す方法。

今回なら、条件はこうなります。
- 1栄養素は必要量以上にする。
- 2上限がある栄養素は、上限を超えない。
- 3そのうえで、食材の合計量をできるだけ少なくする。
つまり「栄養は全部足りている。でも、食べる量はできるだけ少ない」という食事を探します。
4.まず普通に計算してみる
Python の数理最適化ライブラリ「PuLP」を使って計算します。
食材 1 種類あたりの摂取量には、上限 200g を設定しました。1 つの食材だけを大量に食べる答えが出ないようにするためです。

そして、食材の合計重量が最も小さくなる組み合わせを探します。
さあ、どんな食事が出てくるでしょうか。
5.……結果がおかしい

計算結果は、565g。
一見すると「たった 565g で 1 日の必要栄養素を満たせるの?」と驚きます。
ところが、中身を見てみると……
| 食材 | 摂取量 |
|---|---|
| 角砂糖 | 200.0g |
| ひまわり油 | 60.3g |
| りょくとう | 58.8g |
| ごま | 46.6g |
| らっかせい | 39.8g |
| まいたけ | 39.6g |
| その他 | … |
角砂糖200g。
さらに、ひまわり油 60.3g。
これは……食事と呼んでいいのでしょうか。
6.なぜ「角砂糖200g」が選ばれた?
もちろん、数学が間違えたわけではありません。
今回の目的は「食材の合計重量を最小にする」ことでした。そのため、少ない重量で多くのエネルギーを確保できる食品が有利になります。
「健康的でおいしい食事」を探したわけではなく、「設定した条件を満たしながら重量を最小にする組み合わせ」を探した結果、角砂糖が選ばれた。
数学的には正解。でも、人間にはかなり厳しい。
7.数学だけでは「食べられる食事」にならない
ここで問題が分かりました。
「栄養素を満たす」という条件だけでは、「現実的に食べられるか?」という条件が抜けています。
では、1 つの食材を 20g までに制限すればいいのでしょうか。
それだと、今度はご飯のように 200g 程度食べてもおかしくない食品まで、20g しか使えなくなってしまいます。
食材ごとに「この食品なら、このくらいの量までなら現実的」というデータがあれば理想的です。しかし、そのようなデータは見つけられませんでした。
そこで今回は、別の方法を取ります。
8.ここからは人間の判断を入れる
「明らかに現実的ではない食材が選ばれたら、その食材を候補から外して、もう一度計算する」
実際に、調味料系、油脂類、非常に特殊な食材などを除外して再計算しています。
つまりここからは、数学だけでなく、人間の感覚も少し入ります。
そして、もう一度「最強の食事」を計算します。
9.再計算した結果がこちら
| 食材 | 摂取量 |
|---|---|
| 小麦粉 | 200.00g |
| いんげん豆 | 200.00g |
| レンズ豆 | 185.68g |
| モロヘイヤ | 72.76g |
| まいたけ(油いため) | 67.88g |
| らっかせい | 48.67g |
| ドライマンゴー | 36.04g |
| アーモンド | 30.00g |
| まいたけ(乾) | 16.37g |
| なつめやし | 15.63g |
| チアシード | 10.00g |
| 牛レバー | 4.53g |
| アセロラ | 1.44g |
| 刻み昆布 | 0.05g |
……やはり普通の献立とはかなり違います。
でも、最初の「角砂糖200g」よりは、かなり食事らしくなりました。
10.本当に必要な栄養素は全部足りている?
では、この食事で栄養素をどれだけ摂れるのでしょうか。
| 栄養素 | 摂取量 |
|---|---|
| カロリー | 2,650kcal |
| タンパク質 | 136.0g |
| 脂質 | 58.9g |
| 炭水化物 | 457.4g |
| 食物繊維 | 103.3g |
| ビタミンC | 100.0mg |
| カルシウム | 800.0mg |
| 鉄 | 34.0mg |
必要量を下回らないようにしつつ、上限が設定されている栄養素については、上限を超えない結果になっています。
ただし、すべてが必要量ぴったりというわけではありません。葉酸は 784.7、カリウムは 7,050.3、モリブデンは 600.0 と、必要量を大きく上回っている栄養素もあります。
11.「最強の食事」は、本当に最強なのか?
今回分かったのは、少し意外なことでした。
数学的に「栄養を満たす」ことと、人間にとって「良い食事」であることは、同じではありません。
最初の計算では、角砂糖200gという結果が出ました。これは計算としては間違っていません。
ただ、「何を最小化するのか」によって、答えは大きく変わります。
今回は「食材の合計重量」を最小化しました。もし食費を最小化するなら、答えは変わるでしょう。調理時間を最小化するなら、さらに違う答えになるはずです。
つまり「最強の食事」というものは、何を最強と定義するかによって変わります。
12.🔍 ちなみに、どうやって調べた?
ここからは、今回の分析方法について少し詳しく説明します。
「どうやってこの食事を計算したの?」と気になった方はぜひどうぞ。
分析方法をくわしく見る
使用したデータ
本文でも触れたとおり、2 つのデータを組み合わせています。
| データ | 用途 | 規模 |
|---|---|---|
| 日本人の食事摂取基準(厚生労働省) | 1日に必要な栄養素の基準値 | 20代男性の数値を使用 |
| 日本食品標準成分表2020年版 八訂(文部科学省) | 各食品に含まれる栄養素 | 2,478品目 |
なぜこの2つのデータを使った?
今回の問題は、「必要な栄養素」と「食品に含まれる栄養素」を組み合わせることで解けます。
そのため、必要量を厚生労働省から、食品の栄養成分を文部科学省から取り、2 つを突き合わせています。
分析の流れ
- 120代男性の1日に必要な栄養素量を用意する。
- 2各食品の栄養成分データを用意する。
- 3各食品の摂取量を変数として設定する。
- 4必要な栄養素を下回らないように制約を設定する。
- 5上限量がある栄養素は、上限を超えないように制約を設定する。
- 6各食品の摂取量には0〜200gの範囲を設定する。
- 7食材の合計摂取量が最小になる組み合わせをPuLPで求める。
- 8非現実的な食品が含まれた場合、その食品を除外して再計算する。
制約式と目的関数
栄養素ごとに「摂取量が必要量以上になる」という制約を立てます。制約式は栄養素の数だけ並びます。

上限量のある栄養素については、逆向きの制約も加えます。

最小化したのは、各食材の摂取量の合計です。つまり「できるだけ少ない重量で必要な栄養素を満たす」という問題として設定しています。
そのため、これは「食材の種類数を最小化する問題」ではありません。
使用したコード
Python の PuLP を使って実装しています。
from pulp import *
problem = LpProblem(sense=LpMinimize)
P = df.index.to_list()
x = LpVariable.dicts(
'x',
P,
cat='Continuous',
lowBound=0,
upBound=200
)lowBound=0 によって摂取量の下限を 0g にし、upBound=200 によって 1 食品あたりの上限を 200g に設定しています。
そのうえで各栄養素について必要量や上限量の制約を追加し、最後に食品の合計量を最小化します。
problem += lpSum([1*x[p] for p in P])食品の除外について
最初の結果では、角砂糖や油など、現実の食事としては扱いにくい食品が選ばれました。
そこで、食品名に含まれるキーワードなどを利用し、次のような食品を手動で除外しています。
- 調味料系
- 油脂類
- 非常に特殊な食品
- 食べることを想定しにくい食品
これは完全に自動化された判断ではなく、分析者による人間の判断が入っている部分です。
分析上の注意点
今回の結果は「これを食べれば健康になれる理想の献立」ではありません。あくまで、設定した栄養基準・食品データ・制約条件のもとで、食材の合計量を最小化した結果です。
特に、次の点は最適化していません。
- 食材の味
- 調理方法
- 食べやすさ
- 食事の満足感
- 食材価格
- 食材同士の相性
- 食事の継続しやすさ
13.まとめ
- 栄養素の必要量と食品成分のデータを使えば、「栄養を満たす食事」を数理最適化できる
- 非現実的な食品を除外して再計算すると、小麦粉・豆類・モロヘイヤなどを中心とした組み合わせになった
- 「栄養を満たす」と「おいしく、現実的に食べられる」は別問題だった
- 何を「最適」とするかによって、数学が導き出す食事は大きく変わる
14.次に調べたいこと
今回の「最強の食事」は、まだまだ改良できそうです。例えば、
- 食費を最小化したら、どんな食事になる?
- 調理時間を最小化したらどうなる?
- 20代女性や40代男性では結果が変わる?
- 「おいしさ」や「食べやすさ」まで条件に入れたらどうなる?
- 最適化された食材を、実際の料理にするとどんな献立になる?
このあたりまで条件を追加していくと、「数学が考えた最強の食事」から「人間が本当に食べたい最強の食事」へ進化させられそうです。
