79
36

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 3 years have passed since last update.

京大生協の食堂メニューの最適解を探る

Last updated at Posted at 2021-04-16

はじめに

京都大学には「ミールシステム」という悪魔のシステムがあります。

簡単に説明すると学食版の定期みたいなものです。
うかつにもこれに登録してしまうと雨の日も風の日も雪にも夏の暑さにも耐え、例え休日であろうと元を取るために学食へ向かう通称「ミール奴隷」となってしまいます。
(僕は少食である程度自炊もし食堂と下宿の距離がそんなに近いわけでもないのでミールシステムに対するヘイトが多少極端になってるかもしれませんが悪しからず)

一応「バランスの取れた食事を毎日とる」ことを大義名分としているみたいですが学食定期を配っただけでバランスの取れた食事が自動的にとれるようになるわけがありません。

そこで今回少しでも可哀そうなミール奴隷達を救うため食堂メニューの最適解を調べました。

データ

去年はオンライン授業でほとんど学校に行くことが無かったので気付きませんでしたが生協がいつのまにかネットで食堂のメニューを確認出来るようにしていたようです。(chuboz?とかいうシステムを使ってるっぽい?)
http://west2-univ.jp/sp/kyoto-univ.php
ここからスクレイピングさせていただきました。

image.png
学食を買うとメニューによって上の画像のような赤、緑、黄で点数が計算されます。
今回最適解を探るうえで一食の目安を参考にメニューを最適化します。

結果の例としてカフェテリアルネのメニューの表
image.png

コード

方針

ソルバーとしてはPuLPとかいう非常に簡単に問題を設定して最適化してくれるのがあるっぽいのでこれを利用しようと思います。

最適化する問題としては

制約条件

  • 値段>550円
  • 赤の点数>2.7
  • 緑の点数>1.0
  • 黄色の点数>5.7

値段はミール一食分の値段550円を参考に、赤緑黄の点数はレシートにある目安の点数を参考に(僕が男なので男性の目安の点数を採用)条件を設定

問題

値段と、赤緑黄の目安の点数との差の合計を最小化する

コード

上の表をdataframeとして読み込んでいるものとして

まず問題設定の前準備

#最小化問題の設定
problem = pulp.LpProblem("Shokudo")

#注文する個数を表す変数、上限は適当に設定した
df['order_num'] = [pulp.LpVariable(f'{i}ko', 0, 100, "Integer") for i in df.index]

合計金額や点数の合計は要は内積で求めることが出来るので目的関数は以下の通り設定

# 目的関数
    problem += pulp.lpDot(df['price'], df['order_num'])+ (
        pulp.lpDot(df['red_score'], df['order_num'])) + (
                   pulp.lpDot(df['green_score'], df['order_num'])) + (
                   pulp.lpDot(df['yellow_score'], df['order_num']))

上で説明したとおりに制約条件を記述していく

# 制約条件
    problem += pulp.lpDot(df['red_score'], df['order_num']) >= 2.7
    problem += pulp.lpDot(df['green_score'], df['order_num']) >= 1.0
    problem += pulp.lpDot(df['yellow_score'], df['order_num']) >= 5.7
    problem += pulp.lpDot(df['price'], df['order_num']) >= 550

最適化の開始

problem.solve()

df['result'] = df['order_num'].apply(lambda x: pulp.value(x))
print(df[['name', 'price',  "result"]])

結果

京都大学生協ルネカフェテリアのデータの結果

         name  price  result
12  オクラ巣ごもり玉子     88     2.0
18        味噌汁     33     2.0
23  ショコラモンブラン    220     1.0
25        大学芋     88     1.0

以下に各食堂でのメニュー最適解を紹介します
(画像の縮尺は適当)

京都大学生協ルネカフェテリア

image.png
オクラ巣ごもり玉子:2杯
味噌汁:2杯
ショコラモンブラン:1杯
大学芋:1杯
合計金額
550円
赤:2.8
緑:1.0
黄:5.8

味噌汁2杯にショコラモンブランですか……
##京都大学生協中央食堂
image.png
鶏きも煮:1杯
茄子のピリ辛胡麻風味:1杯
海老クリームコロッケ:1杯
ほうれん草塩ナムル:1杯
温泉玉子:1杯
納豆:1個
大学芋:1杯
合計金額
550円
赤:2.7
緑:1.1
黄:5.8

品数的には多いけど全部小鉢なので満足感が無いかも
##京都大学生協南部食堂
image.png
鶏のホワイトシチュー:1杯
海老クリームコロッケ:2杯
だし巻き:1杯
合計金額
550円
赤:3.7
緑:1.3
黄:6.6

金無いの?
##京都大学生協吉田食堂
image.png

鶏から南蛮:1杯
ひじき煮:2杯
南瓜サラダ:1杯
大学芋:1杯
合計金額
550円
赤:2.7
緑:1.5
黄:5.7

久しぶりに「肉」が来た。肉があるだけで見た目の満足感が上がる
##京都大学生協中央食堂
image.png
ローストンカツごまソース:1杯
小松菜わさび和え:1杯
味噌汁:2杯
大学芋:1杯
合計金額
550円
赤:2.7
緑:1.0
黄:5.7

カツがあるだけでもう満足感が得られるので味噌汁が2杯あることについても気にならなくなる
##京都大学生協北部食堂
入れ忘れてました、北部食堂ファンの人ごめんなさい
image.png
ササミチーズカツ:264円:1杯
ほうれん草:66円:1杯
きんぴらごぼう:66円:1杯
南瓜サラダ:66円:1杯
温泉玉子:44円:2杯
合計金額
550円
赤:2.9
緑:1.0
黄:5.7

さすが人気の北食、主食が無いことを除けばなんだか普通に良さそう

考察

大学芋がほとんどのメニューに含まれていた。これは赤緑黄のスコアがカロリーベースで計算されているためである。
image.png
こちらにある通り大学芋は野菜量0gにもかかわらずカロリーが高めであるため緑スコアが0.8も稼げてしまい、多くのメニューで採択されたものと考えられる。

追記:コメントによると緑の分類は決して野菜類を指しているのではなく「体の調子を良くする食材」を指しているため緑に芋も含まれるらしいです。
真にバランスのいい食事をとるには様々なパラメーターを総合的に判断する必要があるということっぽい。バランスのとれた食事って大変だ

79
36
4

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
79
36

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?