青木です。paizaラーニング担当のエンジニアです。
突然ですが、料理をするのって結構手間がかかりますよね。
材料を切ったり量ったり焼いたりしなければならないですし、火を通す料理は完成を待たないといけません。しかもただ待てばいいわけではなく、時間を測っていないと焦げることもあります。
そして何より、料理した結果洗い物が増えるので、あと片付けのことを考えるとさらに億劫になります。
このように料理は自分にとって大変過ぎるので、これまで外食やコンビニ弁当に頼っていました。しかし、在宅勤務で運動不足ということもあり、最近やや健康が不安になってきました。
そこで今回は、健康な食生活に向けての第一歩を踏み出すために、「料理が大変」という問題を自分の得意領域であるプログラミングを使って解決してみようと思います!
※この記事は、「paiza Advent Calendar 2020」の記事を一部編集して公開しています。
取り組む問題のゴール設定
できるだけ料理の手間を避けつつ、健康維持のための栄養素を確実に摂取できる食品の選択方法を検討することを目的とします。
実際の食品データを入力としてプログラムで問題を解き、食べるべき食品を確定します。
食品の選択にあたっては、いくつか条件を設けます。
面倒な料理の工程を避ける
ここでは、料理をする上で手間がかかる工程を「料理っぽい」と表現することにします。
今回、料理っぽい工程はなるべく避けたいと思っています。やや感覚的なものになりますが、具体的には以下のような工程のことです。
- 食材を切る
- 食材に火を通す
- 食材を量る
- 食材を洗う
- 時間を計る
- 汚れる食器や調理器具が増える
たとえば、以下の3つのたまご料理の料理っぽさの強弱関係は次のとおりと定めます。
スクランブルエッグ > ゆでたまご > たまごかけごはん
この中ではたまごかけごはんがもっとも料理っぽさが低いと言えます。
つまり以下のような料理っぽさの低い工程だけを経て、なんとか楽に料理をすることを目指す必要があります。
- 冷凍食品を皿に乗せて電子レンジでチンする
- お湯を沸かしてカップ麺を作る
- 缶詰を開ける(缶切りを使わないプルタブ式の方がなおよい)
- パック納豆に付属しょうゆとからしを入れて混ぜる
- みかんの皮をむく(手は多少汚れるが我慢する)
問題を整理する
「おいしい」や「満腹になる」といったことは考えず、料理っぽさの低い工程だけで作れる、健康に生きていくための栄養を摂取できる食事メニューを考えます。
それを考えるためには食品に含まれる栄養成分の量を知る必要があります。
今回は分かりやすく、栄養成分表示が法律で義務化されている三大栄養素(たんぱく質、脂質、炭水化物)のみを考慮します。
余談ですが、ビタミンやミネラルなどには表示義務はないらしく、書いてない食品が多かったので扱わないこととしました。
たいていの食品には、三大栄養素は多かれ少なかれ含まれています。三大栄養素を必要な量だけ摂取することを考える場合、1つの食品を大量に食べれば簡単に条件を満たすことができてしまいます。
現実には1種類の食品だけを食べ続けるのはつらいですし、問題としてもおもしろくありませんよね。よって、価格が安く条件を満たせるメニューを見つけることとします。
必要栄養素を摂取できる安価な食品を求める
問題
30代男性が1日に必要とする栄養素の摂取量目安は、たんぱく質65g, 脂質30g, 炭水化物65gとされています。(出典:glico「日本人の食事摂取量基準(2020年版)」より)
n種類の食品それぞれについて、1個あたりの税込み価格および栄養成分としてたんぱく質、脂質、炭水化物の量が小数点以下1桁まで与えられます。
それぞれの栄養成分を目安以上摂取するための食品の組合せを求めてください。
このとき目安の量以上でかつ合計価格がなるべく小さくなるものを選ぶものとします。
入力値
入力は食品データです。実際に某スーパーマーケットで16品目を調達してきました。価格と3大栄養素成分を以下に転記します。
名前,価格,タンパク質,脂質,炭水化物 雪印6Pチーズ(1P),35,3.7,4.7,0.6 特から(11個入としたときの1個),55,4.6,3.5,5.2 サトウのごはん新潟県産コシヒカリ(1パック200g),190,4.2,0.0,67.8 ネオレーズンバターロール(1個),28,2.7,8.9,20.5 (冷凍)みなさまのお墨付きほうれん草(1/4袋の50g),51,1.1,0.2,2.6 (冷凍)みなさまのお墨付き高原ブロッコリー(1/5袋の50g),36,1.9,0.2,2.6 (冷凍)炒飯の極みえび五目XO醤(1/2袋の300g),206,14.7,22.5,83.4 じゃがりこサラダ(1カップ60g),107,4.3,14.4,38.1 じゃがりこチーズ(1カップ60g),107,4.8,14.5,35.4 エッセルスーパーカップ超バニラ,105,5.6,23.4,35.3 ヤクルト(1本),40,0.8,0.1,11.5 おかめ納豆極小粒(1パック),31,8.6,4.7,7.8 マウントレーニアエスプレッソ,124,2.7,2.2,16.3 カップヌードル,138,10.5,14.6,44.5 シーフードヌードル,138,8.9,13.6,45.5 (缶詰)いなば商品ライトツナ,107,11.8,18.6,0.1
健康になるという当初の目的をちょっと忘れてしまいお菓子なども入っていますがいったん気にしないでください。
解答例
今回は単純な再帰による総当たりで解くコードをJavaで書いてみました。
入力タブに切り替えていただくと、さきほどの16品目の情報が入力されていることが分かると思います。
実行してみると、以下のとおりになります。
784円 じゃがりこチーズ(1カップ60g)(4.8,14.5,35.4)@107 x 1 (冷凍)みなさまのお墨付き高原ブロッコリー(1/5袋の50g)(1.9,0.2,2.6)@36 x 1 ネオレーズンバターロール(1個)(2.7,8.9,20.5)@28 x 17 特から(11個入としたときの1個)(4.6,3.5,5.2)@55 x 3
じゃがりこ1つ、ブロッコリー1/5袋、レーズンバターロール17個、冷凍唐揚げ3個を食べれば、食事摂取基準料を784円で満たせるようです。
なお、今回はブログ掲載用にオンラインプログラム実行環境のpaiza.IOで動作させているのですが、paiza.IOでは実行時間の制約があるので、16品目すべてを含んだ場合にはタイムアウトして処理しきれません。
そのため入力データの先頭に'#'を挿入して、一部のデータを使わないようにコメントアウトしています。
私の手元の環境では16品目で実行できるため、実行してみた結果を以下に載せておきます。
784円 (缶詰)いなば商品ライトツナ(11.8,18.6,0.1)@107 x 4 シーフードヌードル(8.9,13.6,45.5)@138 x 2 ヤクルト(1本)(0.8,0.1,11.5)@40 x 2
こちらもさきほどと同じ784円の解となり、ツナ缶4つ、カップ麺2つ、ヤクルト2本を摂取すればよいことが分かりました。
入力データをコメントアウトして入れ替えると、結果もそれなりに変化します。「ブロッコリーをひたすら食べる」みたいな結果にならず、それなりに食品がばらけた結果が得られてほっとしています。
皆さんが実行するときはもう少し健康的な食品に変えて試してみてください。
ちなみに今回のコードで使った再帰は、大量データを扱う際に便利ですがプログラミング初心者の方には少し難しいかもしれません。
以下の記事では、再帰関数を使ってプログラミング問題を解く方法を解説していますのでよければ参考にしてください。
今後の課題
今回選択した16品目のラインナップに健康によいもの以外も含まれていたので、健康な食生活を目指すという目的を思い出し、今後改善していければと思います。
機能的にはビタミンや食物繊維を考慮できていないため、それらへの拡張を検討する必要があります。今の問題の定義のままでは、野菜なしでも健康な食生活が送れることになってしまいます。
また、一日は三食ありますので、三食のバランスを見ていい感じにメニューを分ける機能も欲しいところです。
計算コストもやや厳しいです。動的計画法の導入で多少の改善は見込めますが、ビタミンのパラメータや食品の種類が増えると難しいかもしれません。
実は今回の問題は「The Diet Problem」と呼ばれており、線型計画問題として定式化できます。(参考:The Diet Problem | NEOS)
線型計画問題を解くアルゴリズムは広く研究されていて、ソルバプログラムも公開されていますので、もっと実用的なプログラムにするためにはそれらを使うのがよさそうです。(参考:Hands-On Linear Programming: Optimization With Python – Real Python)
なおpaizaラーニングでは、定番のアルゴリズムを学べる動画講座を公開しています。再帰を使って解く問題もありますので、興味がある方はぜひ受講してみてください。
今回はひとまず、数値化して何を食べるかを定量的に決めるためのベースラインを作ることができました。
健康のための一歩を踏み出せたと言ってもよいでしょう!(…たぶん)
「paizaラーニング」では、未経験者でもブラウザさえあれば、今すぐプログラミングの基礎が動画で学べるレッスンを多数公開しております。
詳しくはこちら
そしてpaizaでは、Webサービス開発企業などで求められるコーディング力や、テストケースを想定する力などが問われるプログラミングスキルチェック問題も提供しています。
スキルチェックに挑戦した人は、その結果によってS・A・B・C・D・Eの6段階のランクを取得できます。必要なスキルランクを取得すれば、書類選考なしで企業の求人に応募することも可能です。「自分のプログラミングスキルを客観的に知りたい」「スキルを使って転職したい」という方は、ぜひチャレンジしてみてください。
詳しくはこちら