ピザの味を最大限に引き出すために
Methods to Maximize the Deliciousness of Pizza)
多くの人々はピザに愛着を持っており、辛さやヴィーガン、ベジタリアン、ペスカタリアン、肉食、マキャベリアン、あるいはクラシックなチーズ好きなど、さまざまな好みがあります。
ピザ店オーナーが直面する課題は、ビジネスを運営する複雑さと顧客の多様な好みに対応することです。このジレンマに対処するために、データサイエンティスト(DS)が雇われました。幸運なことに、DSはピザが大好きで、それが仕事の説明として明示されていました。
DSのタスクは解決策を考案することであり、以下に示すのがその結果です。下のグラフは、20種類の異なるピザの具材について、50人の顧客の好みを示しています。データは匿名化されており、ピザにパイナップルやニンニクを好むかどうかなど、顧客の好みは機密となっています。
例えば、顧客0はペパロニ、パイナップル、ニンニクを好み、リコッタチーズ、ベーコン、玉ねぎは嫌いです。この重要な顧客は他の具材には無関心です。そのため、メニューが好みの具材(緑色)を取り入れ、嫌いな具材(赤色)を除外していれば、顧客の満足度は維持されます。
顧客の好みがデータサイエンティスト(DS)によって把握されたことで、次のステップでは問題を数学的に定式化する方法を決定する必要があります。この段階が意思決定の鍵となります。
目的:喜んでいる顧客の数を最大化して顧客の満足度を高める。
制約条件:すべての要件を満たし、厄介な顧客対応は酸っぱい味を残しながらも甘さを要求する苦い果実と同様であることを認識する。
変数:具材の選択(Xe)と満足した顧客(Uc)を示すバイナリ変数を使用する。
{(0, 'L'): {2, 10, 13, 18}, (0, 'D'): {5, 8, 16}, (1, 'L'): set(), (1, 'D'): set(), (2, 'L'): set(), (2, 'D'): {0, 2, 4, 18}, (3, 'L'): {12, 13, 14}, (3, 'D'): set(), (4, 'L'): {15}, (4, 'D'): {4, 5, 6, 18}, (5, 'L'): {0, 8}, (5, 'D'): {6}, (6, 'L'): {0, 13, 14, 17}, (6, 'D'): {4, 7, 10}, (7, 'L'): {1, 6, 10, 14}, (7, 'D'): {15}, (8, 'L'): {3, 7, 18}, (8, 'D'): {4, 5, 9, 15}, (9, 'L'): {10, 15}, (9, 'D'): {8, 14, 18, 19}, (10, 'L'): set(), (10, 'D'): {7, 14, 16}, (11, 'L'): {1, 2, 12}, (11, 'D'): {3, 10}, (12, 'L'): {5, 10, 13}, (12, 'D'): {19}, (13, 'L'): {1, 8, 14, 16}, (13, 'D'): {4, 12}, (14, 'L'): {0, 10, 12}, (14, 'D'): set(), (15, 'L'): {4}, (15, 'D'): {11}, (16, 'L'): set(), (16, 'D'): {3, 8, 10, 17}, (17, 'L'): {0, 11, 17, 18}, (17, 'D'): {6, 19}, (18, 'L'): {17}, (18, 'D'): {9, 11, 19}, (19, 'L'): {4, 7, 11}, (19, 'D'): set(), (20, 'L'): {5, 8, 15, 16}, (20, 'D'): {2, 11}, (21, 'L'): set(), (21, 'D'): {0, 6, 10, 13}, (22, 'L'): {2, 5, 7, 17}, (22, 'D'): {1, 3, 18}, (23, 'L'): {3, 13}, (23, 'D'): {4, 8, 12}, (24, 'L'): {8}, (24, 'D'): {3, 5, 17, 19}, (25, 'L'): {0, 7, 9, 10}, (25, 'D'): {2, 6, 19}, (26, 'L'): {10, 18}, (26, 'D'): {3, 4, 7}, (27, 'L'): {3, 6}, (27, 'D'): {7, 9, 16}, (28, 'L'): {7, 17}, (28, 'D'): set(), (29, 'L'): set(), (29, 'D'): {4, 6}, (30, 'L'): set(), (30, 'D'): {0, 4, 5, 19}, (31, 'L'): {5, 8, 18}, (31, 'D'): {0, 12, 13}, (32, 'L'): {8}, (32, 'D'): {0, 7}, (33, 'L'): set(), (33, 'D'): {0, 2, 12}, (34, 'L'): set(), (34, 'D'): {3, 15}, (35, 'L'): set(), (35, 'D'): set(), (36, 'L'): {7}, (36, 'D'): {0, 19}, (37, 'L'): {6}, (37, 'D'): {5, 13, 15, 19}, (38, 'L'): {7, 9, 16, 18}, (38, 'D'): {3}, (39, 'L'): {10, 13, 17}, (39, 'D'): {15}, (40, 'L'): {9, 15}, (40, 'D'): {8, 10, 18, 19}, (41, 'L'): {0, 5}, (41, 'D'): {14, 16}, (42, 'L'): {4, 13}, (42, 'D'): set(), (43, 'L'): {4, 8, 16}, (43, 'D'): {6, 7, 10}, (44, 'L'): {0, 12}, (44, 'D'): {3, 5, 8, 9}, (45, 'L'): {2, 10}, (45, 'D'): set(), (46, 'L'): {2, 5, 9, 12}, (46, 'D'): set(), (47, 'L'): set(), (47, 'D'): set(), (48, 'L'): {4, 7, 13}, (48, 'D'): {1}, (49, 'L'): {0, 15}, (49, 'D'): {2, 4, 12, 13}}
Pythonのコード
import networkx as nximport matplotlib.pyplot as pltpizza_ingredients = [ 'モッツァレラチーズ', 'トマトソース', 'ペパロニ', 'マッシュルーム', 'ピーマン', '玉ねぎ', '黒オリーブ', 'ソーセージ', 'ベーコン', 'ハム', 'パイナップル', 'ハラペーニョ', '新鮮なバジル', 'にんにく', 'オリーブオイル', 'パルメザンチーズ', 'リコッタチーズ', 'ほうれん草', 'チェリートマト', 'アンチョビ']plt.figure(figsize=(12,6))for c in customers: X= [c for e in elements] Y= [e for e in elements] plt.scatter(X,Y, c='grey', s=20, alpha=0.4) x= [c for e in data[c,'L'] ] y= [e for e in data[c,'L'] ] plt.scatter(x,y, c='g', s=50) x= [c for e in data[c,'D'] ] y= [e for e in data[c,'D'] ] plt.scatter(x,y, c='r', s=50) plt.xticks(list(customers),rotation=90, fontweight='bold')plt.yticks(list(elements), pizza_ingredients, fontweight='bold')plt.ylabel('材料', fontweight='bold')plt.xlabel('顧客', fontweight='bold')plt.tight_layout()fname = 'base_pizza.png'plt.savefig(fname)# ファイルをダウンロードするfiles.download(fname)plt.show()
%pip install ortoolsfrom ortools.sat.python import cp_modeldef main(): model = cp_model.CpModel() X = {e:model.NewBoolVar(f"x_{e}") for e in elements} Sc = {c:model.NewBoolVar(f"S_{c}") for c in customers} for c,v in Sc.items(): for e in data[c,'L']: model.Add(v<= X[e]) for e in data[c,'D']: model.Add(v<= 1-X[e]) expressions = [v for s,v in Sc.items() ] model.Maximize( cp_model.LinearExpr.Sum(expressions)) solver = cp_model.CpSolver() status = solver.Solve(model)
シミュレーション結果:
HAMを含む選択された材料は、横のラインで示され、満足した顧客は縦のゴールデンラインで表されます。例えば、簡単な顧客である顧客1は特定の好みや嫌いな材料はありませんが、顧客28は2つの好きな材料(緑)と嫌いな材料(赤)はありません。
全体の目的関数の合計値は17です。Pythonのコードに興味がある場合は、GitHubリポジトリで見つけることができます。LinkedInではリンク付きの投稿が制限されるため、コメントセクションで見つけてください。すべてのリンクは等しく、しかし一部はより等しいです。
嫌いな材料を完全に無視する:
嫌いな材料の嫌悪感を無視して、許可される材料の数を10に制限すると、結果は次のようになります:
満足した顧客は28人です。
一つの嫌いな材料を許容する:
許可される材料の数を10に制限すると、結果は次のようになります:
満足した顧客は21人です。
好きな材料の数が嫌いな材料よりも多い場合に許容する:
許可される材料の数を10に制限すると、結果は次のようになります:
27満足のお客様。
これは単純化されたシナリオであることを理解しています。実際のピザ店では、さまざまな考慮事項や制約が関与するため、座ってリラックスしてピザをお楽しみください。
この問題は、google-hash-code-2022-practice-problemに触発されました。
Github: https://github.com/OptimizationExpert/Pyomo
お楽しみください!
We will continue to update VoAGI; if you have any questions or suggestions, please contact us!
Was this article helpful?
93 out of 132 found this helpful
Related articles