בקטע הזה נסביר איך לפתור בעיה קלאסית שנקראת דיאטת סטיגלר, שנקראת על שם חתן פרס הנובל לכלכלה, ג'ורג' סטיגלר, שחישב דרך לא יקרה לספק את הצרכים התזונתיים הבסיסיים בהינתן חבילת מאכלים. הוא הגדיר זאת בתור תרגיל מתמטי ולא כהמלצות אכילה, למרות שלאחרונה הרעיון של חישוב תזונה אופטימלית הוא נכנס לאופנה.
בזכות דיאטה של סטיגלר, נדרשה עמידה בסכומים המינימליים האלה:
רשימת המרכיבים התזונתיים
ערכים תזונתיים | הצריכה היומית המומלצת |
---|---|
קלוריות | 3,000 קלוריות |
חלבונים | 70 גרם |
סידן | 8. גרם |
ברזל | 12 מיליגרם |
ויטמין A | 5,000 IU |
תיאמין (ויטמין B1) | 1.8 מיליגרם |
ריבופלאווין (ויטמין B2) | 2.7 מיליגרם |
ויטמין B3 (ניאצין) | 18 מיליגרם |
חומצה אסקורבית (ויטמין C) | 75 מיליגרם |
קבוצת המאכלים שסטיגלר העריכה משקפת את התקופה (1944). הנתונים התזונתיים הבאים הם לדולר ולא ליחידה, כך שהמטרה היא לקבוע כמה דולרים להוציא על כל מוצר מזון.
רשימת סחורות
סחורות | יחידה | מחיר משנת 1939 (סנטים) | קלוריות (קילוקלוריות) | חלבונים (ג') | סידן (ג') | ברזל (מ"ג) | ויטמין A (KIU) | תיאמין (מ"ג) | ריבופלאווין (מ"ג) | ניאצין (מ"ג) | חומצה אסקורבית (מ"ג) |
---|---|---|---|---|---|---|---|---|---|---|---|
קמח חיטה (מועשר) | 10 lb | 36 | 44.7 | 1411 | 2 | 365 | 0 | 55.4 | 33.3 | 441 | 0 |
מקרוני | פאונד אחד | 14.1 | 11.6 | 418 | 0.7 | 54 | 0 | 3.2 | 1.9 | 68 | 0 |
דגני חיטה (מועשר) | 28 אונקיות | 24.2 | 11.8 | 377 | 14.4 | 175 | 0 | 14.4 | 8.8 | 114 | 0 |
פתיתי תירס | 227 ג' | 7.1 | 11.4 | 252 | 0.1 | 56 | 0 | 13.5 | 2.3 | 68 | 0 |
ארוחת תירס | פאונד אחד | 4.6 | 36.0 | 897 | 1.7 | 99 | 30.9 | 17.4 | 7.9 | 106 | 0 |
הומיני גריטס | 24 אונקיות | 8.5 | 28.6 | 680 | 0.8 | 80 | 0 | 10.6 | 1.6 | 110 | 0 |
אורז | פאונד אחד | 7.5 | 21.2 | 460 | 0.6 | 41 | 0 | 2 | 4.8 | 60 | 0 |
שיבולת שועל מגולגלת | פאונד אחד | 7.1 | 18 | 907 | 5.1 | 341 | 0 | 37.1 | 8.9 | 64 | 0 |
לחם לבן (מועשר) | פאונד אחד | 7.9 | 15.0 | 488 | 2.5 | 115 | 0 | 13.8 | 8.5 | 126 | 0 |
לחם מחיטה מלאה | פאונד אחד | 9.1 | 12.2 | 484 | 2.7 | 125 | 0 | 13.9 | 6.4 | 160 | 0 |
לחם שיפון | פאונד אחד | 9.1 | 12.4 | 439 | 1.1 | 82 | 0 | 9.9 | 3 | 66 | 0 |
קילו-עוגה | פאונד אחד | 24.8 | 8.0 | 130 | 0.4 | 31 | 18.9 | 2.8 | 3 | 17 | 0 |
קרקרים | פאונד אחד | 15.1 | 12.5 | 288 | 0.5 | 50 | 0 | 0 | 0 | 0 | 0 |
חלב | 1 רב' | 11 | 6.1 | 310 | 10.5 | 18 | 16.8 | 4 | 16 | 7 | 177 |
חלב מאודה (פחית שימורים) | 14.5 אונקיות | 6.7 | 8.4 | 422 | 15.1 | 9 | 26 | 3 | 23.5 | 11 | 60 |
חמאה | פאונד אחד | 30.8 | 10.8 | 9 | 0.2 | 3 | 44.2 | 0 | 0.2 | 2 | 0 |
אולאומרגרינה | פאונד אחד | 16.1 | 20.6 | 17 | 0.6 | 6 | 55.8 | 0.2 | 0 | 0 | 0 |
ביצים | דוז אחד. | 32.6 | 18 | 238 | 1.0 | 52 | 18.6 | 2.8 | 6.5 | 1 | 0 |
גבינה (צ'דר) | פאונד אחד | 24.2 | 7.4 | 448 | 16.4 | 19 | 28.1 | 0.8 | 10.3 | 4 | 0 |
קרם | 1/2 נק' | 14.1 | 3.5 | 49 | 1.7 | 3 | 16.9 | 0.6 | 2.5 | 0 | 17 |
חמאת בוטנים | פאונד אחד | 17.9 | 15.7 | 661 | 1.0 | 48 | 0 | 9.6 | 8.1 | 471 | 0 |
מיונז | 1/2 נק' | 16.7 | 8.6 | 18 | 0.2 | 8 | 2.7 | 0.4 | 0.5 | 0 | 0 |
קריסקו | פאונד אחד | 20.3 | 20.1 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 |
שומן חזיר | פאונד אחד | 9.8 | 41.7 | 0 | 0 | 0 | 0.2 | 0 | 0.5 | 5 | 0 |
סטייק סירלוין | פאונד אחד | 39.6 | 18 | 166 | 0.1 | 34 | 0.2 | 2.1 | 18 | 69 | 0 |
סטייק עגול | פאונד אחד | 36.4 | 2.2 | 214 | 0.1 | 32 | 0.4 | 2.5 | 2.4 | 87 | 0 |
צלי צלעות | פאונד אחד | 29.2 | 3.4 | 213 | 0.1 | 33 | 0 | 0 | 2 | 0 | 0 |
צ'אק צ'י | פאונד אחד | 22.6 | 3.6 | 309 | 0.2 | 46 | 0.4 | 1 | 4 | 120 | 0 |
צלחת | פאונד אחד | 14.6 | 8.5 | 404 | 0.2 | 62 | 0 | 0.9 | 0 | 0 | 0 |
כבד (בקר) | פאונד אחד | 26.8 | 2.2 | 333 | 0.2 | 139 | 169.2 | 6.4 | 21.0 | 316 | 525 |
שוק כבש | פאונד אחד | 27.6 | 3.1 | 245 | 0.1 | 20 | 0 | 2.8 | 3.9 | 86 | 0 |
נתחי טלה (צלע) | פאונד אחד | 18 | 3.3 | 140 | 0.1 | 15 | 0 | 1.7 | 2.7 | 54 | 0 |
נתחי בשר חזיר | פאונד אחד | 30.7 | 3.5 | 196 | 0.2 | 30 | 0 | 17.4 | 2.7 | 60 | 0 |
צלי חזיר | פאונד אחד | 24.2 | 4.4 | 249 | 0.3 | 37 | 0 | 18.2 | 3.6 | 79 | 0 |
בייקון | פאונד אחד | 25.6 | 10.4 | 152 | 0.2 | 23 | 0 | 1.8 | 1.8 | 71 | 0 |
בשר חזיר, מעושן | פאונד אחד | 27.4 | 6.7 | 212 | 0.2 | 31 | 0 | 9.9 | 3.3 | 50 | 0 |
בשר חזיר מלוח | פאונד אחד | 16 | 18.8 | 164 | 0.1 | 26 | 0 | 1.4 | 1.8 | 0 | 0 |
עוף צלוי | פאונד אחד | 30.3 | 1.8 | 184 | 0.1 | 30 | 0.1 | 0.9 | 1.8 | 68 | 46 |
קציצות עגל | פאונד אחד | 42.3 | 1.7 | 156 | 0.1 | 24 | 0 | 1.4 | 2.4 | 57 | 0 |
סלמון, ורוד (פחית שימורים) | 16 אונקיות | 13 | 5.8 | 705 | 6.8 | 45 | 3.5 | 1 | 4.9 | 209 | 0 |
תפוחים | פאונד אחד | 4.4 | 5.8 | 27 | 0.5 | 36 | 7.3 | 3.6 | 2.7 | 5 | 544 |
בננות | פאונד אחד | 6.1 | 4.9 | 60 | 0.4 | 30 | 17.4 | 2.5 | 3.5 | 28 | 498 |
לימונים | דוז אחד. | 26 | 1.0 | 21 | 0.5 | 14 | 0 | 0.5 | 0 | 4 | 952 |
תפוזים | דוז אחד. | 30.9 | 2.2 | 40 | 1.1 | 18 | 11.1 | 3.6 | 1.3 | 10 | 1998 |
שעועית ירוקה | פאונד אחד | 7.1 | 2.4 | 138 | 3.7 | 80 | 69 | 4.3 | 5.8 | 37 | 862 |
כרוב | פאונד אחד | 3.7 | 2.6 | 125 | 4.0 | 36 | 7.2 | 9 | 4.5 | 26 | 5369 |
גזרים | חבילה אחת | 4.7 | 2.7 | 73 | 2.8 | 43 | 188.5 | 6.1 | 4.3 | 89 | 608 |
סלרי | מוט מעקב אחד | 7.3 | 0.9 | 51 | 3.0 | 23 | 0.9 | 1.4 | 1.4 | 9 | 313 |
חסה | ראש אחד | 8.2 | 0.4 | 27 | 1.1 | 22 | 112.4 | 1.8 | 3.4 | 11 | 449 |
בצלים | פאונד אחד | 3.6 | 5.8 | 166 | 3.8 | 59 | 16.6 | 4.7 | 5.9 | 21 | 1184 |
תפוחי אדמה | 6 ק"ג | 34 | 14.3 | 336 | 1.8 | 118 | 6.7 | 29.4 | 7.1 | 198 | 2522 |
תרד | פאונד אחד | 8.1 | 1.1 | 106 | 0 | 138 | 918.4 | 5.7 | 13.8 | 33 | 2755 |
בטטה | פאונד אחד | 5.1 | 9.6 | 138 | 2.7 | 54 | 290.7 | 8.4 | 5.4 | 83 | 1912 |
אפרסקים (פחית שימורים) | מס' 2 1/2 | 16.8 | 3.7 | 20 | 0.4 | 10 | 21.5 | 0.5 | 1 | 31 | 196 |
אגסים (פחית שימורים) | מס' 2 1/2 | 20.4 | 3.0 | 8 | 0.3 | 8 | 0.8 | 0.8 | 0.8 | 5 | 81 |
אננס (פחית) | מס' 2 1/2 | 21.3 | 2.4 | 16 | 0.4 | 8 | 2 | 2.8 | 0.8 | 7 | 399 |
אספרגוס (פחית שימורים) | מס' 2 | 27.7 | 0.4 | 33 | 0.3 | 12 | 16.3 | 1.4 | 2.1 | 17 | 272 |
שעועית ירוקה (פחית שימורים) | מס' 2 | 10 | 1.0 | 54 | 2 | 65 | 53.9 | 1.6 | 4.3 | 32 | 431 |
בשר חזיר ושעועית (פחית שימורים) | 16 אונקיות | 7.1 | 7.5 | 364 | 4 | 134 | 3.5 | 8.3 | 7.7 | 56 | 0 |
תירס (פחית שימורים) | מס' 2 | 10.4 | 5.2 | 136 | 0.2 | 16 | 12 | 1.6 | 2.7 | 42 | 218 |
אפונה (פחית שימורים) | מס' 2 | 13.8 | 2.3 | 136 | 0.6 | 45 | 34.9 | 4.9 | 2.5 | 37 | 370 |
עגבניות (פחית שימורים) | מס' 2 | 8.6 | 1.3 | 63 | 0.7 | 38 | 53.2 | 3.4 | 2.5 | 36 | 1253 |
מרק עגבניות (פחית שימורים) | 10 1/2 oz. | 7.6 | 1.6 | 71 | 0.6 | 43 | 57.9 | 3.5 | 2.4 | 67 | 862 |
אפרסקים, מיובשים | פאונד אחד | 15.7 | 8.5 | 87 | 1.7 | 173 | 86.8 | 1.2 | 4.3 | 55 | 57 |
שזיפים מיובשים | פאונד אחד | 9 | 12.8 | 99 | 2.5 | 154 | 85.7 | 3.9 | 4.3 | 65 | 257 |
צימוקים, מיובשים | 15 אונקיות | 9.4 | 13.5 | 104 | 2.5 | 136 | 4.5 | 6.3 | 1.4 | 24 | 136 |
אפונה, מיובשת | פאונד אחד | 7.9 | 20.0 | 1367 | 4.2 | 345 | 18 | 28.7 | 18.4 | 162 | 0 |
שעועית לימה מיובשת | פאונד אחד | 8.9 | 17.4 | 1055 | 3.7 | 459 | 5.1 | 26.9 | 38.2 | 93 | 0 |
שעועית כהה, מיובשת | פאונד אחד | 5.9 | 26.9 | 1691 | 11.4 | 792 | 0 | 38.4 | 24.6 | 217 | 0 |
קפה | פאונד אחד | 22.4 | 0 | 0 | 0 | 0 | 0 | 4 | 5.1 | 50 | 0 |
תה | 1/4 פאונד | 17.4 | 0 | 0 | 0 | 0 | 0 | 0 | 2.3 | 42 | 0 |
קקאו | 227 ג' | 8.6 | 8.7 | 237 | 3 | 72 | 0 | 2 | 11.9 | 40 | 0 |
שוקולד | 227 ג' | 16.2 | 8.0 | 77 | 1.3 | 39 | 0 | 0.9 | 3.4 | 14 | 0 |
סוכר | 10 lb | 51.7 | 34.9 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 |
סירופ תירס | 24 אונקיות | 13.7 | 14.7 | 0 | 0.5 | 74 | 0 | 0 | 0 | 5 | 0 |
דבשה | 18 אונקיות | 13.6 | 9.0 | 0 | 10.3 | 244 | 0 | 1.9 | 7.5 | 146 | 0 |
שימורי תות | פאונד אחד | 20.5 | 6.4 | 11 | 0.4 | 7 | 0.2 | 0.2 | 0.4 | 3 | 0 |
מכיוון שכל החומרים המזינים עברו נירמול על ידי המחיר, המטרה שלנו היא פשוט למזער את כמות המזונות.
בשנת 1944, סטיגלר חישב את התשובה הטובה ביותר שיכלה לכך, תוך ציון עצב:
...לא נראה שיש שיטה ישירה כלשהי למציאת המינימום של פונקציה לינארית בכפוף לתנאים ליניאריים.
הוא מצא דיאטה שעולה 39.93 $בשנה, ב-1939 דולר. בשנת 1947, ג'ק לדרמן השתמש בשיטת החלק הזה (ואז, המצאה חדשה!) כדי לקבוע את הפתרון האופטימלי. נדרשו 120 ימים של תשעה פקידים על מחשבונים והגיעו לתשובה.
פתרון באמצעות הפותר הלינארי
בקטעים הבאים מוצגת תוכנית לפתרון בעיית הדיאטה של סטיגלר.
ייבוא ה-wrapper של הפותר הלינארי
מייבאים את ה-wrapper של הפותר הלינארי של OR-Tools, ממשק של הפותר הלינארי של [GLOP](/Optimization/mip/glop0, כפי שמוצג בהמשך.
Python
from ortools.linear_solver import pywraplp
C++
#include <array> #include <memory> #include <string> #include <utility> // std::pair #include <vector> #include "absl/flags/flag.h" #include "absl/log/flags.h" #include "ortools/base/init_google.h" #include "ortools/base/logging.h" #include "ortools/linear_solver/linear_solver.h"
Java
import com.google.ortools.Loader; import com.google.ortools.linearsolver.MPConstraint; import com.google.ortools.linearsolver.MPObjective; import com.google.ortools.linearsolver.MPSolver; import com.google.ortools.linearsolver.MPVariable; import java.util.ArrayList; import java.util.List;
C#
using System; using System.Collections.Generic; using Google.OrTools.LinearSolver;
נתונים לגבי הבעיה
הקוד הבא יוצר מערך nutrients
עבור הדרישות המינימליות לחומרים המזינים, ומערך data
עבור טבלת הנתונים התזונתיים בכל תמיסה.
Python
# Nutrient minimums. nutrients = [ ["Calories (kcal)", 3], ["Protein (g)", 70], ["Calcium (g)", 0.8], ["Iron (mg)", 12], ["Vitamin A (KIU)", 5], ["Vitamin B1 (mg)", 1.8], ["Vitamin B2 (mg)", 2.7], ["Niacin (mg)", 18], ["Vitamin C (mg)", 75], ] # Commodity, Unit, 1939 price (cents), Calories (kcal), Protein (g), # Calcium (g), Iron (mg), Vitamin A (KIU), Vitamin B1 (mg), Vitamin B2 (mg), # Niacin (mg), Vitamin C (mg) data = [ # fmt: off ['Wheat Flour (Enriched)', '10 lb.', 36, 44.7, 1411, 2, 365, 0, 55.4, 33.3, 441, 0], ['Macaroni', '1 lb.', 14.1, 11.6, 418, 0.7, 54, 0, 3.2, 1.9, 68, 0], ['Wheat Cereal (Enriched)', '28 oz.', 24.2, 11.8, 377, 14.4, 175, 0, 14.4, 8.8, 114, 0], ['Corn Flakes', '8 oz.', 7.1, 11.4, 252, 0.1, 56, 0, 13.5, 2.3, 68, 0], ['Corn Meal', '1 lb.', 4.6, 36.0, 897, 1.7, 99, 30.9, 17.4, 7.9, 106, 0], ['Hominy Grits', '24 oz.', 8.5, 28.6, 680, 0.8, 80, 0, 10.6, 1.6, 110, 0], ['Rice', '1 lb.', 7.5, 21.2, 460, 0.6, 41, 0, 2, 4.8, 60, 0], ['Rolled Oats', '1 lb.', 7.1, 25.3, 907, 5.1, 341, 0, 37.1, 8.9, 64, 0], ['White Bread (Enriched)', '1 lb.', 7.9, 15.0, 488, 2.5, 115, 0, 13.8, 8.5, 126, 0], ['Whole Wheat Bread', '1 lb.', 9.1, 12.2, 484, 2.7, 125, 0, 13.9, 6.4, 160, 0], ['Rye Bread', '1 lb.', 9.1, 12.4, 439, 1.1, 82, 0, 9.9, 3, 66, 0], ['Pound Cake', '1 lb.', 24.8, 8.0, 130, 0.4, 31, 18.9, 2.8, 3, 17, 0], ['Soda Crackers', '1 lb.', 15.1, 12.5, 288, 0.5, 50, 0, 0, 0, 0, 0], ['Milk', '1 qt.', 11, 6.1, 310, 10.5, 18, 16.8, 4, 16, 7, 177], ['Evaporated Milk (can)', '14.5 oz.', 6.7, 8.4, 422, 15.1, 9, 26, 3, 23.5, 11, 60], ['Butter', '1 lb.', 30.8, 10.8, 9, 0.2, 3, 44.2, 0, 0.2, 2, 0], ['Oleomargarine', '1 lb.', 16.1, 20.6, 17, 0.6, 6, 55.8, 0.2, 0, 0, 0], ['Eggs', '1 doz.', 32.6, 2.9, 238, 1.0, 52, 18.6, 2.8, 6.5, 1, 0], ['Cheese (Cheddar)', '1 lb.', 24.2, 7.4, 448, 16.4, 19, 28.1, 0.8, 10.3, 4, 0], ['Cream', '1/2 pt.', 14.1, 3.5, 49, 1.7, 3, 16.9, 0.6, 2.5, 0, 17], ['Peanut Butter', '1 lb.', 17.9, 15.7, 661, 1.0, 48, 0, 9.6, 8.1, 471, 0], ['Mayonnaise', '1/2 pt.', 16.7, 8.6, 18, 0.2, 8, 2.7, 0.4, 0.5, 0, 0], ['Crisco', '1 lb.', 20.3, 20.1, 0, 0, 0, 0, 0, 0, 0, 0], ['Lard', '1 lb.', 9.8, 41.7, 0, 0, 0, 0.2, 0, 0.5, 5, 0], ['Sirloin Steak', '1 lb.', 39.6, 2.9, 166, 0.1, 34, 0.2, 2.1, 2.9, 69, 0], ['Round Steak', '1 lb.', 36.4, 2.2, 214, 0.1, 32, 0.4, 2.5, 2.4, 87, 0], ['Rib Roast', '1 lb.', 29.2, 3.4, 213, 0.1, 33, 0, 0, 2, 0, 0], ['Chuck Roast', '1 lb.', 22.6, 3.6, 309, 0.2, 46, 0.4, 1, 4, 120, 0], ['Plate', '1 lb.', 14.6, 8.5, 404, 0.2, 62, 0, 0.9, 0, 0, 0], ['Liver (Beef)', '1 lb.', 26.8, 2.2, 333, 0.2, 139, 169.2, 6.4, 50.8, 316, 525], ['Leg of Lamb', '1 lb.', 27.6, 3.1, 245, 0.1, 20, 0, 2.8, 3.9, 86, 0], ['Lamb Chops (Rib)', '1 lb.', 36.6, 3.3, 140, 0.1, 15, 0, 1.7, 2.7, 54, 0], ['Pork Chops', '1 lb.', 30.7, 3.5, 196, 0.2, 30, 0, 17.4, 2.7, 60, 0], ['Pork Loin Roast', '1 lb.', 24.2, 4.4, 249, 0.3, 37, 0, 18.2, 3.6, 79, 0], ['Bacon', '1 lb.', 25.6, 10.4, 152, 0.2, 23, 0, 1.8, 1.8, 71, 0], ['Ham, smoked', '1 lb.', 27.4, 6.7, 212, 0.2, 31, 0, 9.9, 3.3, 50, 0], ['Salt Pork', '1 lb.', 16, 18.8, 164, 0.1, 26, 0, 1.4, 1.8, 0, 0], ['Roasting Chicken', '1 lb.', 30.3, 1.8, 184, 0.1, 30, 0.1, 0.9, 1.8, 68, 46], ['Veal Cutlets', '1 lb.', 42.3, 1.7, 156, 0.1, 24, 0, 1.4, 2.4, 57, 0], ['Salmon, Pink (can)', '16 oz.', 13, 5.8, 705, 6.8, 45, 3.5, 1, 4.9, 209, 0], ['Apples', '1 lb.', 4.4, 5.8, 27, 0.5, 36, 7.3, 3.6, 2.7, 5, 544], ['Bananas', '1 lb.', 6.1, 4.9, 60, 0.4, 30, 17.4, 2.5, 3.5, 28, 498], ['Lemons', '1 doz.', 26, 1.0, 21, 0.5, 14, 0, 0.5, 0, 4, 952], ['Oranges', '1 doz.', 30.9, 2.2, 40, 1.1, 18, 11.1, 3.6, 1.3, 10, 1998], ['Green Beans', '1 lb.', 7.1, 2.4, 138, 3.7, 80, 69, 4.3, 5.8, 37, 862], ['Cabbage', '1 lb.', 3.7, 2.6, 125, 4.0, 36, 7.2, 9, 4.5, 26, 5369], ['Carrots', '1 bunch', 4.7, 2.7, 73, 2.8, 43, 188.5, 6.1, 4.3, 89, 608], ['Celery', '1 stalk', 7.3, 0.9, 51, 3.0, 23, 0.9, 1.4, 1.4, 9, 313], ['Lettuce', '1 head', 8.2, 0.4, 27, 1.1, 22, 112.4, 1.8, 3.4, 11, 449], ['Onions', '1 lb.', 3.6, 5.8, 166, 3.8, 59, 16.6, 4.7, 5.9, 21, 1184], ['Potatoes', '15 lb.', 34, 14.3, 336, 1.8, 118, 6.7, 29.4, 7.1, 198, 2522], ['Spinach', '1 lb.', 8.1, 1.1, 106, 0, 138, 918.4, 5.7, 13.8, 33, 2755], ['Sweet Potatoes', '1 lb.', 5.1, 9.6, 138, 2.7, 54, 290.7, 8.4, 5.4, 83, 1912], ['Peaches (can)', 'No. 2 1/2', 16.8, 3.7, 20, 0.4, 10, 21.5, 0.5, 1, 31, 196], ['Pears (can)', 'No. 2 1/2', 20.4, 3.0, 8, 0.3, 8, 0.8, 0.8, 0.8, 5, 81], ['Pineapple (can)', 'No. 2 1/2', 21.3, 2.4, 16, 0.4, 8, 2, 2.8, 0.8, 7, 399], ['Asparagus (can)', 'No. 2', 27.7, 0.4, 33, 0.3, 12, 16.3, 1.4, 2.1, 17, 272], ['Green Beans (can)', 'No. 2', 10, 1.0, 54, 2, 65, 53.9, 1.6, 4.3, 32, 431], ['Pork and Beans (can)', '16 oz.', 7.1, 7.5, 364, 4, 134, 3.5, 8.3, 7.7, 56, 0], ['Corn (can)', 'No. 2', 10.4, 5.2, 136, 0.2, 16, 12, 1.6, 2.7, 42, 218], ['Peas (can)', 'No. 2', 13.8, 2.3, 136, 0.6, 45, 34.9, 4.9, 2.5, 37, 370], ['Tomatoes (can)', 'No. 2', 8.6, 1.3, 63, 0.7, 38, 53.2, 3.4, 2.5, 36, 1253], ['Tomato Soup (can)', '10 1/2 oz.', 7.6, 1.6, 71, 0.6, 43, 57.9, 3.5, 2.4, 67, 862], ['Peaches, Dried', '1 lb.', 15.7, 8.5, 87, 1.7, 173, 86.8, 1.2, 4.3, 55, 57], ['Prunes, Dried', '1 lb.', 9, 12.8, 99, 2.5, 154, 85.7, 3.9, 4.3, 65, 257], ['Raisins, Dried', '15 oz.', 9.4, 13.5, 104, 2.5, 136, 4.5, 6.3, 1.4, 24, 136], ['Peas, Dried', '1 lb.', 7.9, 20.0, 1367, 4.2, 345, 2.9, 28.7, 18.4, 162, 0], ['Lima Beans, Dried', '1 lb.', 8.9, 17.4, 1055, 3.7, 459, 5.1, 26.9, 38.2, 93, 0], ['Navy Beans, Dried', '1 lb.', 5.9, 26.9, 1691, 11.4, 792, 0, 38.4, 24.6, 217, 0], ['Coffee', '1 lb.', 22.4, 0, 0, 0, 0, 0, 4, 5.1, 50, 0], ['Tea', '1/4 lb.', 17.4, 0, 0, 0, 0, 0, 0, 2.3, 42, 0], ['Cocoa', '8 oz.', 8.6, 8.7, 237, 3, 72, 0, 2, 11.9, 40, 0], ['Chocolate', '8 oz.', 16.2, 8.0, 77, 1.3, 39, 0, 0.9, 3.4, 14, 0], ['Sugar', '10 lb.', 51.7, 34.9, 0, 0, 0, 0, 0, 0, 0, 0], ['Corn Syrup', '24 oz.', 13.7, 14.7, 0, 0.5, 74, 0, 0, 0, 5, 0], ['Molasses', '18 oz.', 13.6, 9.0, 0, 10.3, 244, 0, 1.9, 7.5, 146, 0], ['Strawberry Preserves', '1 lb.', 20.5, 6.4, 11, 0.4, 7, 0.2, 0.2, 0.4, 3, 0], # fmt: on ]
C++
// Nutrient minimums. const std::vector<std::pair<std::string, double>> nutrients = { {"Calories (kcal)", 3.0}, {"Protein (g)", 70.0}, {"Calcium (g)", 0.8}, {"Iron (mg)", 12.0}, {"Vitamin A (kIU)", 5.0}, {"Vitamin B1 (mg)", 1.8}, {"Vitamin B2 (mg)", 2.7}, {"Niacin (mg)", 18.0}, {"Vitamin C (mg)", 75.0}}; struct Commodity { std::string name; //!< Commodity name std::string unit; //!< Unit double price; //!< 1939 price per unit (cents) //! Calories (kcal), //! Protein (g), //! Calcium (g), //! Iron (mg), //! Vitamin A (kIU), //! Vitamin B1 (mg), //! Vitamin B2 (mg), //! Niacin (mg), //! Vitamin C (mg) std::array<double, 9> nutrients; }; std::vector<Commodity> data = { {"Wheat Flour (Enriched)", "10 lb.", 36, {44.7, 1411, 2, 365, 0, 55.4, 33.3, 441, 0}}, {"Macaroni", "1 lb.", 14.1, {11.6, 418, 0.7, 54, 0, 3.2, 1.9, 68, 0}}, {"Wheat Cereal (Enriched)", "28 oz.", 24.2, {11.8, 377, 14.4, 175, 0, 14.4, 8.8, 114, 0}}, {"Corn Flakes", "8 oz.", 7.1, {11.4, 252, 0.1, 56, 0, 13.5, 2.3, 68, 0}}, {"Corn Meal", "1 lb.", 4.6, {36.0, 897, 1.7, 99, 30.9, 17.4, 7.9, 106, 0}}, {"Hominy Grits", "24 oz.", 8.5, {28.6, 680, 0.8, 80, 0, 10.6, 1.6, 110, 0}}, {"Rice", "1 lb.", 7.5, {21.2, 460, 0.6, 41, 0, 2, 4.8, 60, 0}}, {"Rolled Oats", "1 lb.", 7.1, {25.3, 907, 5.1, 341, 0, 37.1, 8.9, 64, 0}}, {"White Bread (Enriched)", "1 lb.", 7.9, {15.0, 488, 2.5, 115, 0, 13.8, 8.5, 126, 0}}, {"Whole Wheat Bread", "1 lb.", 9.1, {12.2, 484, 2.7, 125, 0, 13.9, 6.4, 160, 0}}, {"Rye Bread", "1 lb.", 9.1, {12.4, 439, 1.1, 82, 0, 9.9, 3, 66, 0}}, {"Pound Cake", "1 lb.", 24.8, {8.0, 130, 0.4, 31, 18.9, 2.8, 3, 17, 0}}, {"Soda Crackers", "1 lb.", 15.1, {12.5, 288, 0.5, 50, 0, 0, 0, 0, 0}}, {"Milk", "1 qt.", 11, {6.1, 310, 10.5, 18, 16.8, 4, 16, 7, 177}}, {"Evaporated Milk (can)", "14.5 oz.", 6.7, {8.4, 422, 15.1, 9, 26, 3, 23.5, 11, 60}}, {"Butter", "1 lb.", 30.8, {10.8, 9, 0.2, 3, 44.2, 0, 0.2, 2, 0}}, {"Oleomargarine", "1 lb.", 16.1, {20.6, 17, 0.6, 6, 55.8, 0.2, 0, 0, 0}}, {"Eggs", "1 doz.", 32.6, {2.9, 238, 1.0, 52, 18.6, 2.8, 6.5, 1, 0}}, {"Cheese (Cheddar)", "1 lb.", 24.2, {7.4, 448, 16.4, 19, 28.1, 0.8, 10.3, 4, 0}}, {"Cream", "1/2 pt.", 14.1, {3.5, 49, 1.7, 3, 16.9, 0.6, 2.5, 0, 17}}, {"Peanut Butter", "1 lb.", 17.9, {15.7, 661, 1.0, 48, 0, 9.6, 8.1, 471, 0}}, {"Mayonnaise", "1/2 pt.", 16.7, {8.6, 18, 0.2, 8, 2.7, 0.4, 0.5, 0, 0}}, {"Crisco", "1 lb.", 20.3, {20.1, 0, 0, 0, 0, 0, 0, 0, 0}}, {"Lard", "1 lb.", 9.8, {41.7, 0, 0, 0, 0.2, 0, 0.5, 5, 0}}, {"Sirloin Steak", "1 lb.", 39.6, {2.9, 166, 0.1, 34, 0.2, 2.1, 2.9, 69, 0}}, {"Round Steak", "1 lb.", 36.4, {2.2, 214, 0.1, 32, 0.4, 2.5, 2.4, 87, 0}}, {"Rib Roast", "1 lb.", 29.2, {3.4, 213, 0.1, 33, 0, 0, 2, 0, 0}}, {"Chuck Roast", "1 lb.", 22.6, {3.6, 309, 0.2, 46, 0.4, 1, 4, 120, 0}}, {"Plate", "1 lb.", 14.6, {8.5, 404, 0.2, 62, 0, 0.9, 0, 0, 0}}, {"Liver (Beef)", "1 lb.", 26.8, {2.2, 333, 0.2, 139, 169.2, 6.4, 50.8, 316, 525}}, {"Leg of Lamb", "1 lb.", 27.6, {3.1, 245, 0.1, 20, 0, 2.8, 3.9, 86, 0}}, {"Lamb Chops (Rib)", "1 lb.", 36.6, {3.3, 140, 0.1, 15, 0, 1.7, 2.7, 54, 0}}, {"Pork Chops", "1 lb.", 30.7, {3.5, 196, 0.2, 30, 0, 17.4, 2.7, 60, 0}}, {"Pork Loin Roast", "1 lb.", 24.2, {4.4, 249, 0.3, 37, 0, 18.2, 3.6, 79, 0}}, {"Bacon", "1 lb.", 25.6, {10.4, 152, 0.2, 23, 0, 1.8, 1.8, 71, 0}}, {"Ham, smoked", "1 lb.", 27.4, {6.7, 212, 0.2, 31, 0, 9.9, 3.3, 50, 0}}, {"Salt Pork", "1 lb.", 16, {18.8, 164, 0.1, 26, 0, 1.4, 1.8, 0, 0}}, {"Roasting Chicken", "1 lb.", 30.3, {1.8, 184, 0.1, 30, 0.1, 0.9, 1.8, 68, 46}}, {"Veal Cutlets", "1 lb.", 42.3, {1.7, 156, 0.1, 24, 0, 1.4, 2.4, 57, 0}}, {"Salmon, Pink (can)", "16 oz.", 13, {5.8, 705, 6.8, 45, 3.5, 1, 4.9, 209, 0}}, {"Apples", "1 lb.", 4.4, {5.8, 27, 0.5, 36, 7.3, 3.6, 2.7, 5, 544}}, {"Bananas", "1 lb.", 6.1, {4.9, 60, 0.4, 30, 17.4, 2.5, 3.5, 28, 498}}, {"Lemons", "1 doz.", 26, {1.0, 21, 0.5, 14, 0, 0.5, 0, 4, 952}}, {"Oranges", "1 doz.", 30.9, {2.2, 40, 1.1, 18, 11.1, 3.6, 1.3, 10, 1998}}, {"Green Beans", "1 lb.", 7.1, {2.4, 138, 3.7, 80, 69, 4.3, 5.8, 37, 862}}, {"Cabbage", "1 lb.", 3.7, {2.6, 125, 4.0, 36, 7.2, 9, 4.5, 26, 5369}}, {"Carrots", "1 bunch", 4.7, {2.7, 73, 2.8, 43, 188.5, 6.1, 4.3, 89, 608}}, {"Celery", "1 stalk", 7.3, {0.9, 51, 3.0, 23, 0.9, 1.4, 1.4, 9, 313}}, {"Lettuce", "1 head", 8.2, {0.4, 27, 1.1, 22, 112.4, 1.8, 3.4, 11, 449}}, {"Onions", "1 lb.", 3.6, {5.8, 166, 3.8, 59, 16.6, 4.7, 5.9, 21, 1184}}, {"Potatoes", "15 lb.", 34, {14.3, 336, 1.8, 118, 6.7, 29.4, 7.1, 198, 2522}}, {"Spinach", "1 lb.", 8.1, {1.1, 106, 0, 138, 918.4, 5.7, 13.8, 33, 2755}}, {"Sweet Potatoes", "1 lb.", 5.1, {9.6, 138, 2.7, 54, 290.7, 8.4, 5.4, 83, 1912}}, {"Peaches (can)", "No. 2 1/2", 16.8, {3.7, 20, 0.4, 10, 21.5, 0.5, 1, 31, 196}}, {"Pears (can)", "No. 2 1/2", 20.4, {3.0, 8, 0.3, 8, 0.8, 0.8, 0.8, 5, 81}}, {"Pineapple (can)", "No. 2 1/2", 21.3, {2.4, 16, 0.4, 8, 2, 2.8, 0.8, 7, 399}}, {"Asparagus (can)", "No. 2", 27.7, {0.4, 33, 0.3, 12, 16.3, 1.4, 2.1, 17, 272}}, {"Green Beans (can)", "No. 2", 10, {1.0, 54, 2, 65, 53.9, 1.6, 4.3, 32, 431}}, {"Pork and Beans (can)", "16 oz.", 7.1, {7.5, 364, 4, 134, 3.5, 8.3, 7.7, 56, 0}}, {"Corn (can)", "No. 2", 10.4, {5.2, 136, 0.2, 16, 12, 1.6, 2.7, 42, 218}}, {"Peas (can)", "No. 2", 13.8, {2.3, 136, 0.6, 45, 34.9, 4.9, 2.5, 37, 370}}, {"Tomatoes (can)", "No. 2", 8.6, {1.3, 63, 0.7, 38, 53.2, 3.4, 2.5, 36, 1253}}, {"Tomato Soup (can)", "10 1/2 oz.", 7.6, {1.6, 71, 0.6, 43, 57.9, 3.5, 2.4, 67, 862}}, {"Peaches, Dried", "1 lb.", 15.7, {8.5, 87, 1.7, 173, 86.8, 1.2, 4.3, 55, 57}}, {"Prunes, Dried", "1 lb.", 9, {12.8, 99, 2.5, 154, 85.7, 3.9, 4.3, 65, 257}}, {"Raisins, Dried", "15 oz.", 9.4, {13.5, 104, 2.5, 136, 4.5, 6.3, 1.4, 24, 136}}, {"Peas, Dried", "1 lb.", 7.9, {20.0, 1367, 4.2, 345, 2.9, 28.7, 18.4, 162, 0}}, {"Lima Beans, Dried", "1 lb.", 8.9, {17.4, 1055, 3.7, 459, 5.1, 26.9, 38.2, 93, 0}}, {"Navy Beans, Dried", "1 lb.", 5.9, {26.9, 1691, 11.4, 792, 0, 38.4, 24.6, 217, 0}}, {"Coffee", "1 lb.", 22.4, {0, 0, 0, 0, 0, 4, 5.1, 50, 0}}, {"Tea", "1/4 lb.", 17.4, {0, 0, 0, 0, 0, 0, 2.3, 42, 0}}, {"Cocoa", "8 oz.", 8.6, {8.7, 237, 3, 72, 0, 2, 11.9, 40, 0}}, {"Chocolate", "8 oz.", 16.2, {8.0, 77, 1.3, 39, 0, 0.9, 3.4, 14, 0}}, {"Sugar", "10 lb.", 51.7, {34.9, 0, 0, 0, 0, 0, 0, 0, 0}}, {"Corn Syrup", "24 oz.", 13.7, {14.7, 0, 0.5, 74, 0, 0, 0, 5, 0}}, {"Molasses", "18 oz.", 13.6, {9.0, 0, 10.3, 244, 0, 1.9, 7.5, 146, 0}}, {"Strawberry Preserves", "1 lb.", 20.5, {6.4, 11, 0.4, 7, 0.2, 0.2, 0.4, 3, 0}}};
Java
// Nutrient minimums. List<Object[]> nutrients = new ArrayList<>(); nutrients.add(new Object[] {"Calories (kcal)", 3.0}); nutrients.add(new Object[] {"Protein (g)", 70.0}); nutrients.add(new Object[] {"Calcium (g)", 0.8}); nutrients.add(new Object[] {"Iron (mg)", 12.0}); nutrients.add(new Object[] {"Vitamin A (kIU)", 5.0}); nutrients.add(new Object[] {"Vitamin B1 (mg)", 1.8}); nutrients.add(new Object[] {"Vitamin B2 (mg)", 2.7}); nutrients.add(new Object[] {"Niacin (mg)", 18.0}); nutrients.add(new Object[] {"Vitamin C (mg)", 75.0}); List<Object[]> data = new ArrayList<>(); data.add(new Object[] {"Wheat Flour (Enriched)", "10 lb.", 36, new double[] {44.7, 1411, 2, 365, 0, 55.4, 33.3, 441, 0}}); data.add(new Object[] { "Macaroni", "1 lb.", 14.1, new double[] {11.6, 418, 0.7, 54, 0, 3.2, 1.9, 68, 0}}); data.add(new Object[] {"Wheat Cereal (Enriched)", "28 oz.", 24.2, new double[] {11.8, 377, 14.4, 175, 0, 14.4, 8.8, 114, 0}}); data.add(new Object[] { "Corn Flakes", "8 oz.", 7.1, new double[] {11.4, 252, 0.1, 56, 0, 13.5, 2.3, 68, 0}}); data.add(new Object[] { "Corn Meal", "1 lb.", 4.6, new double[] {36.0, 897, 1.7, 99, 30.9, 17.4, 7.9, 106, 0}}); data.add(new Object[] { "Hominy Grits", "24 oz.", 8.5, new double[] {28.6, 680, 0.8, 80, 0, 10.6, 1.6, 110, 0}}); data.add( new Object[] {"Rice", "1 lb.", 7.5, new double[] {21.2, 460, 0.6, 41, 0, 2, 4.8, 60, 0}}); data.add(new Object[] { "Rolled Oats", "1 lb.", 7.1, new double[] {25.3, 907, 5.1, 341, 0, 37.1, 8.9, 64, 0}}); data.add(new Object[] {"White Bread (Enriched)", "1 lb.", 7.9, new double[] {15.0, 488, 2.5, 115, 0, 13.8, 8.5, 126, 0}}); data.add(new Object[] {"Whole Wheat Bread", "1 lb.", 9.1, new double[] {12.2, 484, 2.7, 125, 0, 13.9, 6.4, 160, 0}}); data.add(new Object[] { "Rye Bread", "1 lb.", 9.1, new double[] {12.4, 439, 1.1, 82, 0, 9.9, 3, 66, 0}}); data.add(new Object[] { "Pound Cake", "1 lb.", 24.8, new double[] {8.0, 130, 0.4, 31, 18.9, 2.8, 3, 17, 0}}); data.add(new Object[] { "Soda Crackers", "1 lb.", 15.1, new double[] {12.5, 288, 0.5, 50, 0, 0, 0, 0, 0}}); data.add( new Object[] {"Milk", "1 qt.", 11, new double[] {6.1, 310, 10.5, 18, 16.8, 4, 16, 7, 177}}); data.add(new Object[] {"Evaporated Milk (can)", "14.5 oz.", 6.7, new double[] {8.4, 422, 15.1, 9, 26, 3, 23.5, 11, 60}}); data.add( new Object[] {"Butter", "1 lb.", 30.8, new double[] {10.8, 9, 0.2, 3, 44.2, 0, 0.2, 2, 0}}); data.add(new Object[] { "Oleomargarine", "1 lb.", 16.1, new double[] {20.6, 17, 0.6, 6, 55.8, 0.2, 0, 0, 0}}); data.add(new Object[] { "Eggs", "1 doz.", 32.6, new double[] {2.9, 238, 1.0, 52, 18.6, 2.8, 6.5, 1, 0}}); data.add(new Object[] {"Cheese (Cheddar)", "1 lb.", 24.2, new double[] {7.4, 448, 16.4, 19, 28.1, 0.8, 10.3, 4, 0}}); data.add(new Object[] { "Cream", "1/2 pt.", 14.1, new double[] {3.5, 49, 1.7, 3, 16.9, 0.6, 2.5, 0, 17}}); data.add(new Object[] { "Peanut Butter", "1 lb.", 17.9, new double[] {15.7, 661, 1.0, 48, 0, 9.6, 8.1, 471, 0}}); data.add(new Object[] { "Mayonnaise", "1/2 pt.", 16.7, new double[] {8.6, 18, 0.2, 8, 2.7, 0.4, 0.5, 0, 0}}); data.add(new Object[] {"Crisco", "1 lb.", 20.3, new double[] {20.1, 0, 0, 0, 0, 0, 0, 0, 0}}); data.add(new Object[] {"Lard", "1 lb.", 9.8, new double[] {41.7, 0, 0, 0, 0.2, 0, 0.5, 5, 0}}); data.add(new Object[] { "Sirloin Steak", "1 lb.", 39.6, new double[] {2.9, 166, 0.1, 34, 0.2, 2.1, 2.9, 69, 0}}); data.add(new Object[] { "Round Steak", "1 lb.", 36.4, new double[] {2.2, 214, 0.1, 32, 0.4, 2.5, 2.4, 87, 0}}); data.add( new Object[] {"Rib Roast", "1 lb.", 29.2, new double[] {3.4, 213, 0.1, 33, 0, 0, 2, 0, 0}}); data.add(new Object[] { "Chuck Roast", "1 lb.", 22.6, new double[] {3.6, 309, 0.2, 46, 0.4, 1, 4, 120, 0}}); data.add( new Object[] {"Plate", "1 lb.", 14.6, new double[] {8.5, 404, 0.2, 62, 0, 0.9, 0, 0, 0}}); data.add(new Object[] {"Liver (Beef)", "1 lb.", 26.8, new double[] {2.2, 333, 0.2, 139, 169.2, 6.4, 50.8, 316, 525}}); data.add(new Object[] { "Leg of Lamb", "1 lb.", 27.6, new double[] {3.1, 245, 0.1, 20, 0, 2.8, 3.9, 86, 0}}); data.add(new Object[] { "Lamb Chops (Rib)", "1 lb.", 36.6, new double[] {3.3, 140, 0.1, 15, 0, 1.7, 2.7, 54, 0}}); data.add(new Object[] { "Pork Chops", "1 lb.", 30.7, new double[] {3.5, 196, 0.2, 30, 0, 17.4, 2.7, 60, 0}}); data.add(new Object[] { "Pork Loin Roast", "1 lb.", 24.2, new double[] {4.4, 249, 0.3, 37, 0, 18.2, 3.6, 79, 0}}); data.add(new Object[] { "Bacon", "1 lb.", 25.6, new double[] {10.4, 152, 0.2, 23, 0, 1.8, 1.8, 71, 0}}); data.add(new Object[] { "Ham, smoked", "1 lb.", 27.4, new double[] {6.7, 212, 0.2, 31, 0, 9.9, 3.3, 50, 0}}); data.add(new Object[] { "Salt Pork", "1 lb.", 16, new double[] {18.8, 164, 0.1, 26, 0, 1.4, 1.8, 0, 0}}); data.add(new Object[] {"Roasting Chicken", "1 lb.", 30.3, new double[] {1.8, 184, 0.1, 30, 0.1, 0.9, 1.8, 68, 46}}); data.add(new Object[] { "Veal Cutlets", "1 lb.", 42.3, new double[] {1.7, 156, 0.1, 24, 0, 1.4, 2.4, 57, 0}}); data.add(new Object[] { "Salmon, Pink (can)", "16 oz.", 13, new double[] {5.8, 705, 6.8, 45, 3.5, 1, 4.9, 209, 0}}); data.add(new Object[] { "Apples", "1 lb.", 4.4, new double[] {5.8, 27, 0.5, 36, 7.3, 3.6, 2.7, 5, 544}}); data.add(new Object[] { "Bananas", "1 lb.", 6.1, new double[] {4.9, 60, 0.4, 30, 17.4, 2.5, 3.5, 28, 498}}); data.add( new Object[] {"Lemons", "1 doz.", 26, new double[] {1.0, 21, 0.5, 14, 0, 0.5, 0, 4, 952}}); data.add(new Object[] { "Oranges", "1 doz.", 30.9, new double[] {2.2, 40, 1.1, 18, 11.1, 3.6, 1.3, 10, 1998}}); data.add(new Object[] { "Green Beans", "1 lb.", 7.1, new double[] {2.4, 138, 3.7, 80, 69, 4.3, 5.8, 37, 862}}); data.add(new Object[] { "Cabbage", "1 lb.", 3.7, new double[] {2.6, 125, 4.0, 36, 7.2, 9, 4.5, 26, 5369}}); data.add(new Object[] { "Carrots", "1 bunch", 4.7, new double[] {2.7, 73, 2.8, 43, 188.5, 6.1, 4.3, 89, 608}}); data.add(new Object[] { "Celery", "1 stalk", 7.3, new double[] {0.9, 51, 3.0, 23, 0.9, 1.4, 1.4, 9, 313}}); data.add(new Object[] { "Lettuce", "1 head", 8.2, new double[] {0.4, 27, 1.1, 22, 112.4, 1.8, 3.4, 11, 449}}); data.add(new Object[] { "Onions", "1 lb.", 3.6, new double[] {5.8, 166, 3.8, 59, 16.6, 4.7, 5.9, 21, 1184}}); data.add(new Object[] { "Potatoes", "15 lb.", 34, new double[] {14.3, 336, 1.8, 118, 6.7, 29.4, 7.1, 198, 2522}}); data.add(new Object[] { "Spinach", "1 lb.", 8.1, new double[] {1.1, 106, 0, 138, 918.4, 5.7, 13.8, 33, 2755}}); data.add(new Object[] {"Sweet Potatoes", "1 lb.", 5.1, new double[] {9.6, 138, 2.7, 54, 290.7, 8.4, 5.4, 83, 1912}}); data.add(new Object[] {"Peaches (can)", "No. 2 1/2", 16.8, new double[] {3.7, 20, 0.4, 10, 21.5, 0.5, 1, 31, 196}}); data.add(new Object[] { "Pears (can)", "No. 2 1/2", 20.4, new double[] {3.0, 8, 0.3, 8, 0.8, 0.8, 0.8, 5, 81}}); data.add(new Object[] { "Pineapple (can)", "No. 2 1/2", 21.3, new double[] {2.4, 16, 0.4, 8, 2, 2.8, 0.8, 7, 399}}); data.add(new Object[] {"Asparagus (can)", "No. 2", 27.7, new double[] {0.4, 33, 0.3, 12, 16.3, 1.4, 2.1, 17, 272}}); data.add(new Object[] { "Green Beans (can)", "No. 2", 10, new double[] {1.0, 54, 2, 65, 53.9, 1.6, 4.3, 32, 431}}); data.add(new Object[] {"Pork and Beans (can)", "16 oz.", 7.1, new double[] {7.5, 364, 4, 134, 3.5, 8.3, 7.7, 56, 0}}); data.add(new Object[] { "Corn (can)", "No. 2", 10.4, new double[] {5.2, 136, 0.2, 16, 12, 1.6, 2.7, 42, 218}}); data.add(new Object[] { "Peas (can)", "No. 2", 13.8, new double[] {2.3, 136, 0.6, 45, 34.9, 4.9, 2.5, 37, 370}}); data.add(new Object[] { "Tomatoes (can)", "No. 2", 8.6, new double[] {1.3, 63, 0.7, 38, 53.2, 3.4, 2.5, 36, 1253}}); data.add(new Object[] {"Tomato Soup (can)", "10 1/2 oz.", 7.6, new double[] {1.6, 71, 0.6, 43, 57.9, 3.5, 2.4, 67, 862}}); data.add(new Object[] { "Peaches, Dried", "1 lb.", 15.7, new double[] {8.5, 87, 1.7, 173, 86.8, 1.2, 4.3, 55, 57}}); data.add(new Object[] { "Prunes, Dried", "1 lb.", 9, new double[] {12.8, 99, 2.5, 154, 85.7, 3.9, 4.3, 65, 257}}); data.add(new Object[] {"Raisins, Dried", "15 oz.", 9.4, new double[] {13.5, 104, 2.5, 136, 4.5, 6.3, 1.4, 24, 136}}); data.add(new Object[] { "Peas, Dried", "1 lb.", 7.9, new double[] {20.0, 1367, 4.2, 345, 2.9, 28.7, 18.4, 162, 0}}); data.add(new Object[] {"Lima Beans, Dried", "1 lb.", 8.9, new double[] {17.4, 1055, 3.7, 459, 5.1, 26.9, 38.2, 93, 0}}); data.add(new Object[] {"Navy Beans, Dried", "1 lb.", 5.9, new double[] {26.9, 1691, 11.4, 792, 0, 38.4, 24.6, 217, 0}}); data.add(new Object[] {"Coffee", "1 lb.", 22.4, new double[] {0, 0, 0, 0, 0, 4, 5.1, 50, 0}}); data.add(new Object[] {"Tea", "1/4 lb.", 17.4, new double[] {0, 0, 0, 0, 0, 0, 2.3, 42, 0}}); data.add( new Object[] {"Cocoa", "8 oz.", 8.6, new double[] {8.7, 237, 3, 72, 0, 2, 11.9, 40, 0}}); data.add(new Object[] { "Chocolate", "8 oz.", 16.2, new double[] {8.0, 77, 1.3, 39, 0, 0.9, 3.4, 14, 0}}); data.add(new Object[] {"Sugar", "10 lb.", 51.7, new double[] {34.9, 0, 0, 0, 0, 0, 0, 0, 0}}); data.add(new Object[] { "Corn Syrup", "24 oz.", 13.7, new double[] {14.7, 0, 0.5, 74, 0, 0, 0, 5, 0}}); data.add(new Object[] { "Molasses", "18 oz.", 13.6, new double[] {9.0, 0, 10.3, 244, 0, 1.9, 7.5, 146, 0}}); data.add(new Object[] {"Strawberry Preserves", "1 lb.", 20.5, new double[] {6.4, 11, 0.4, 7, 0.2, 0.2, 0.4, 3, 0}});
C#
// Nutrient minimums. (String Name, double Value)[] nutrients = new[] { ("Calories (kcal)", 3.0), ("Protein (g)", 70.0), ("Calcium (g)", 0.8), ("Iron (mg)", 12.0), ("Vitamin A (kIU)", 5.0), ("Vitamin B1 (mg)", 1.8), ("Vitamin B2 (mg)", 2.7), ("Niacin (mg)", 18.0), ("Vitamin C (mg)", 75.0) }; (String Name, String Unit, double Price, double[] Nutrients)[] data = new[] { ("Wheat Flour (Enriched)", "10 lb.", 36, new double[] { 44.7, 1411, 2, 365, 0, 55.4, 33.3, 441, 0 }), ("Macaroni", "1 lb.", 14.1, new double[] { 11.6, 418, 0.7, 54, 0, 3.2, 1.9, 68, 0 }), ("Wheat Cereal (Enriched)", "28 oz.", 24.2, new double[] { 11.8, 377, 14.4, 175, 0, 14.4, 8.8, 114, 0 }), ("Corn Flakes", "8 oz.", 7.1, new double[] { 11.4, 252, 0.1, 56, 0, 13.5, 2.3, 68, 0 }), ("Corn Meal", "1 lb.", 4.6, new double[] { 36.0, 897, 1.7, 99, 30.9, 17.4, 7.9, 106, 0 }), ("Hominy Grits", "24 oz.", 8.5, new double[] { 28.6, 680, 0.8, 80, 0, 10.6, 1.6, 110, 0 }), ("Rice", "1 lb.", 7.5, new double[] { 21.2, 460, 0.6, 41, 0, 2, 4.8, 60, 0 }), ("Rolled Oats", "1 lb.", 7.1, new double[] { 25.3, 907, 5.1, 341, 0, 37.1, 8.9, 64, 0 }), ("White Bread (Enriched)", "1 lb.", 7.9, new double[] { 15.0, 488, 2.5, 115, 0, 13.8, 8.5, 126, 0 }), ("Whole Wheat Bread", "1 lb.", 9.1, new double[] { 12.2, 484, 2.7, 125, 0, 13.9, 6.4, 160, 0 }), ("Rye Bread", "1 lb.", 9.1, new double[] { 12.4, 439, 1.1, 82, 0, 9.9, 3, 66, 0 }), ("Pound Cake", "1 lb.", 24.8, new double[] { 8.0, 130, 0.4, 31, 18.9, 2.8, 3, 17, 0 }), ("Soda Crackers", "1 lb.", 15.1, new double[] { 12.5, 288, 0.5, 50, 0, 0, 0, 0, 0 }), ("Milk", "1 qt.", 11, new double[] { 6.1, 310, 10.5, 18, 16.8, 4, 16, 7, 177 }), ("Evaporated Milk (can)", "14.5 oz.", 6.7, new double[] { 8.4, 422, 15.1, 9, 26, 3, 23.5, 11, 60 }), ("Butter", "1 lb.", 30.8, new double[] { 10.8, 9, 0.2, 3, 44.2, 0, 0.2, 2, 0 }), ("Oleomargarine", "1 lb.", 16.1, new double[] { 20.6, 17, 0.6, 6, 55.8, 0.2, 0, 0, 0 }), ("Eggs", "1 doz.", 32.6, new double[] { 2.9, 238, 1.0, 52, 18.6, 2.8, 6.5, 1, 0 }), ("Cheese (Cheddar)", "1 lb.", 24.2, new double[] { 7.4, 448, 16.4, 19, 28.1, 0.8, 10.3, 4, 0 }), ("Cream", "1/2 pt.", 14.1, new double[] { 3.5, 49, 1.7, 3, 16.9, 0.6, 2.5, 0, 17 }), ("Peanut Butter", "1 lb.", 17.9, new double[] { 15.7, 661, 1.0, 48, 0, 9.6, 8.1, 471, 0 }), ("Mayonnaise", "1/2 pt.", 16.7, new double[] { 8.6, 18, 0.2, 8, 2.7, 0.4, 0.5, 0, 0 }), ("Crisco", "1 lb.", 20.3, new double[] { 20.1, 0, 0, 0, 0, 0, 0, 0, 0 }), ("Lard", "1 lb.", 9.8, new double[] { 41.7, 0, 0, 0, 0.2, 0, 0.5, 5, 0 }), ("Sirloin Steak", "1 lb.", 39.6, new double[] { 2.9, 166, 0.1, 34, 0.2, 2.1, 2.9, 69, 0 }), ("Round Steak", "1 lb.", 36.4, new double[] { 2.2, 214, 0.1, 32, 0.4, 2.5, 2.4, 87, 0 }), ("Rib Roast", "1 lb.", 29.2, new double[] { 3.4, 213, 0.1, 33, 0, 0, 2, 0, 0 }), ("Chuck Roast", "1 lb.", 22.6, new double[] { 3.6, 309, 0.2, 46, 0.4, 1, 4, 120, 0 }), ("Plate", "1 lb.", 14.6, new double[] { 8.5, 404, 0.2, 62, 0, 0.9, 0, 0, 0 }), ("Liver (Beef)", "1 lb.", 26.8, new double[] { 2.2, 333, 0.2, 139, 169.2, 6.4, 50.8, 316, 525 }), ("Leg of Lamb", "1 lb.", 27.6, new double[] { 3.1, 245, 0.1, 20, 0, 2.8, 3.9, 86, 0 }), ("Lamb Chops (Rib)", "1 lb.", 36.6, new double[] { 3.3, 140, 0.1, 15, 0, 1.7, 2.7, 54, 0 }), ("Pork Chops", "1 lb.", 30.7, new double[] { 3.5, 196, 0.2, 30, 0, 17.4, 2.7, 60, 0 }), ("Pork Loin Roast", "1 lb.", 24.2, new double[] { 4.4, 249, 0.3, 37, 0, 18.2, 3.6, 79, 0 }), ("Bacon", "1 lb.", 25.6, new double[] { 10.4, 152, 0.2, 23, 0, 1.8, 1.8, 71, 0 }), ("Ham, smoked", "1 lb.", 27.4, new double[] { 6.7, 212, 0.2, 31, 0, 9.9, 3.3, 50, 0 }), ("Salt Pork", "1 lb.", 16, new double[] { 18.8, 164, 0.1, 26, 0, 1.4, 1.8, 0, 0 }), ("Roasting Chicken", "1 lb.", 30.3, new double[] { 1.8, 184, 0.1, 30, 0.1, 0.9, 1.8, 68, 46 }), ("Veal Cutlets", "1 lb.", 42.3, new double[] { 1.7, 156, 0.1, 24, 0, 1.4, 2.4, 57, 0 }), ("Salmon, Pink (can)", "16 oz.", 13, new double[] { 5.8, 705, 6.8, 45, 3.5, 1, 4.9, 209, 0 }), ("Apples", "1 lb.", 4.4, new double[] { 5.8, 27, 0.5, 36, 7.3, 3.6, 2.7, 5, 544 }), ("Bananas", "1 lb.", 6.1, new double[] { 4.9, 60, 0.4, 30, 17.4, 2.5, 3.5, 28, 498 }), ("Lemons", "1 doz.", 26, new double[] { 1.0, 21, 0.5, 14, 0, 0.5, 0, 4, 952 }), ("Oranges", "1 doz.", 30.9, new double[] { 2.2, 40, 1.1, 18, 11.1, 3.6, 1.3, 10, 1998 }), ("Green Beans", "1 lb.", 7.1, new double[] { 2.4, 138, 3.7, 80, 69, 4.3, 5.8, 37, 862 }), ("Cabbage", "1 lb.", 3.7, new double[] { 2.6, 125, 4.0, 36, 7.2, 9, 4.5, 26, 5369 }), ("Carrots", "1 bunch", 4.7, new double[] { 2.7, 73, 2.8, 43, 188.5, 6.1, 4.3, 89, 608 }), ("Celery", "1 stalk", 7.3, new double[] { 0.9, 51, 3.0, 23, 0.9, 1.4, 1.4, 9, 313 }), ("Lettuce", "1 head", 8.2, new double[] { 0.4, 27, 1.1, 22, 112.4, 1.8, 3.4, 11, 449 }), ("Onions", "1 lb.", 3.6, new double[] { 5.8, 166, 3.8, 59, 16.6, 4.7, 5.9, 21, 1184 }), ("Potatoes", "15 lb.", 34, new double[] { 14.3, 336, 1.8, 118, 6.7, 29.4, 7.1, 198, 2522 }), ("Spinach", "1 lb.", 8.1, new double[] { 1.1, 106, 0, 138, 918.4, 5.7, 13.8, 33, 2755 }), ("Sweet Potatoes", "1 lb.", 5.1, new double[] { 9.6, 138, 2.7, 54, 290.7, 8.4, 5.4, 83, 1912 }), ("Peaches (can)", "No. 2 1/2", 16.8, new double[] { 3.7, 20, 0.4, 10, 21.5, 0.5, 1, 31, 196 }), ("Pears (can)", "No. 2 1/2", 20.4, new double[] { 3.0, 8, 0.3, 8, 0.8, 0.8, 0.8, 5, 81 }), ("Pineapple (can)", "No. 2 1/2", 21.3, new double[] { 2.4, 16, 0.4, 8, 2, 2.8, 0.8, 7, 399 }), ("Asparagus (can)", "No. 2", 27.7, new double[] { 0.4, 33, 0.3, 12, 16.3, 1.4, 2.1, 17, 272 }), ("Green Beans (can)", "No. 2", 10, new double[] { 1.0, 54, 2, 65, 53.9, 1.6, 4.3, 32, 431 }), ("Pork and Beans (can)", "16 oz.", 7.1, new double[] { 7.5, 364, 4, 134, 3.5, 8.3, 7.7, 56, 0 }), ("Corn (can)", "No. 2", 10.4, new double[] { 5.2, 136, 0.2, 16, 12, 1.6, 2.7, 42, 218 }), ("Peas (can)", "No. 2", 13.8, new double[] { 2.3, 136, 0.6, 45, 34.9, 4.9, 2.5, 37, 370 }), ("Tomatoes (can)", "No. 2", 8.6, new double[] { 1.3, 63, 0.7, 38, 53.2, 3.4, 2.5, 36, 1253 }), ("Tomato Soup (can)", "10 1/2 oz.", 7.6, new double[] { 1.6, 71, 0.6, 43, 57.9, 3.5, 2.4, 67, 862 }), ("Peaches, Dried", "1 lb.", 15.7, new double[] { 8.5, 87, 1.7, 173, 86.8, 1.2, 4.3, 55, 57 }), ("Prunes, Dried", "1 lb.", 9, new double[] { 12.8, 99, 2.5, 154, 85.7, 3.9, 4.3, 65, 257 }), ("Raisins, Dried", "15 oz.", 9.4, new double[] { 13.5, 104, 2.5, 136, 4.5, 6.3, 1.4, 24, 136 }), ("Peas, Dried", "1 lb.", 7.9, new double[] { 20.0, 1367, 4.2, 345, 2.9, 28.7, 18.4, 162, 0 }), ("Lima Beans, Dried", "1 lb.", 8.9, new double[] { 17.4, 1055, 3.7, 459, 5.1, 26.9, 38.2, 93, 0 }), ("Navy Beans, Dried", "1 lb.", 5.9, new double[] { 26.9, 1691, 11.4, 792, 0, 38.4, 24.6, 217, 0 }), ("Coffee", "1 lb.", 22.4, new double[] { 0, 0, 0, 0, 0, 4, 5.1, 50, 0 }), ("Tea", "1/4 lb.", 17.4, new double[] { 0, 0, 0, 0, 0, 0, 2.3, 42, 0 }), ("Cocoa", "8 oz.", 8.6, new double[] { 8.7, 237, 3, 72, 0, 2, 11.9, 40, 0 }), ("Chocolate", "8 oz.", 16.2, new double[] { 8.0, 77, 1.3, 39, 0, 0.9, 3.4, 14, 0 }), ("Sugar", "10 lb.", 51.7, new double[] { 34.9, 0, 0, 0, 0, 0, 0, 0, 0 }), ("Corn Syrup", "24 oz.", 13.7, new double[] { 14.7, 0, 0.5, 74, 0, 0, 0, 5, 0 }), ("Molasses", "18 oz.", 13.6, new double[] { 9.0, 0, 10.3, 244, 0, 1.9, 7.5, 146, 0 }), ("Strawberry Preserves", "1 lb.", 20.5, new double[] { 6.4, 11, 0.4, 7, 0.2, 0.2, 0.4, 3, 0 }) };
הצהרה על פותר הבעיות LP
הקוד הבא יוצר את ה-wrapper של MPsolver
.
Python
# Instantiate a Glop solver and naming it. solver = pywraplp.Solver.CreateSolver("GLOP") if not solver: return
C++
// Create the linear solver with the GLOP backend. std::unique_ptr<MPSolver> solver(MPSolver::CreateSolver("GLOP"));
Java
// Create the linear solver with the GLOP backend. MPSolver solver = MPSolver.createSolver("GLOP"); if (solver == null) { System.out.println("Could not create solver GLOP"); return; }
C#
// Create the linear solver with the GLOP backend. Solver solver = Solver.CreateSolver("GLOP"); if (solver is null) { return; }
יצירת המשתנים
הקוד הבא יוצר את המשתנים עבור הבעיה.
Python
# Declare an array to hold our variables. foods = [solver.NumVar(0.0, solver.infinity(), item[0]) for item in data] print("Number of variables =", solver.NumVariables())
C++
std::vector<MPVariable*> foods; const double infinity = solver->infinity(); for (const Commodity& commodity : data) { foods.push_back(solver->MakeNumVar(0.0, infinity, commodity.name)); } LOG(INFO) << "Number of variables = " << solver->NumVariables();
Java
double infinity = java.lang.Double.POSITIVE_INFINITY; List<MPVariable> foods = new ArrayList<>(); for (int i = 0; i < data.size(); ++i) { foods.add(solver.makeNumVar(0.0, infinity, (String) data.get(i)[0])); } System.out.println("Number of variables = " + solver.numVariables());
C#
List<Variable> foods = new List<Variable>(); for (int i = 0; i < data.Length; ++i) { foods.Add(solver.MakeNumVar(0.0, double.PositiveInfinity, data[i].Name)); } Console.WriteLine($"Number of variables = {solver.NumVariables()}");
השיטה MakeNumVar
יוצרת משתנה אחד, food[i]
, לכל שורה בטבלה.
כפי שצוין קודם, הנתונים התזונתיים הם לדולר, לכן food[i]
הוא סכום הכסף שצריך להוציא על מוצר i
.
מגדירים את האילוצים
האילוצים בדיאטת סטיגלר מחייבים שהסכום הכולל של החומרים המזינים שכלולים בכל מאכלים יהיה לפחות הדרישה המינימלית לכל אחד מהחומרים המזינים.
בשלב הבא, אנחנו כותבים את האילוצים האלה כאי-שוויונים שכוללים את המערכים data
ו-nutrients
ואת המשתנים food[i]
.
ראשית, כמות הערכים התזונתיים i
שסופקו על ידי מזון j
לדולר היא
data[j][i+3]
(אנחנו מוסיפים 3 לאינדקס העמודות כי הנתונים התזונתיים מתחילים בעמודה הרביעית של data
). מאחר שסכום ההוצאה על מזון j
הוא food[j]
, כמות המרכיבים התזונתיים i
שמסופקת על ידי מזון j
היא
\(data[j][i+3] \cdot food[j]\).
לבסוף, מכיוון שהדרישה המינימלית לחומר התזונתי i
היא nutrients[i][1]
, אנחנו יכולים לכתוב את האילוץ i באופן הבא:
Python
# Create the constraints, one per nutrient. constraints = [] for i, nutrient in enumerate(nutrients): constraints.append(solver.Constraint(nutrient[1], solver.infinity())) for j, item in enumerate(data): constraints[i].SetCoefficient(foods[j], item[i + 3]) print("Number of constraints =", solver.NumConstraints())
C++
// Create the constraints, one per nutrient. std::vector<MPConstraint*> constraints; for (std::size_t i = 0; i < nutrients.size(); ++i) { constraints.push_back( solver->MakeRowConstraint(nutrients[i].second, infinity)); for (std::size_t j = 0; j < data.size(); ++j) { constraints.back()->SetCoefficient(foods[j], data[j].nutrients[i]); } } LOG(INFO) << "Number of constraints = " << solver->NumConstraints();
Java
MPConstraint[] constraints = new MPConstraint[nutrients.size()]; for (int i = 0; i < nutrients.size(); ++i) { constraints[i] = solver.makeConstraint( (double) nutrients.get(i)[1], infinity, (String) nutrients.get(i)[0]); for (int j = 0; j < data.size(); ++j) { constraints[i].setCoefficient(foods.get(j), ((double[]) data.get(j)[3])[i]); } // constraints.add(constraint); } System.out.println("Number of constraints = " + solver.numConstraints());
C#
List<Constraint> constraints = new List<Constraint>(); for (int i = 0; i < nutrients.Length; ++i) { Constraint constraint = solver.MakeConstraint(nutrients[i].Value, double.PositiveInfinity, nutrients[i].Name); for (int j = 0; j < data.Length; ++j) { constraint.SetCoefficient(foods[j], data[j].Nutrients[i]); } constraints.Add(constraint); } Console.WriteLine($"Number of constraints = {solver.NumConstraints()}");
שיטת Python Constraint
(בהתאם לשיטת C++ MakeRowConstraint
) יוצרת את האילוצים של הבעיה. לכל i
,
constraint(nutrients[i][1], solver.infinity)
הפעולה הזו יוצרת אילוץ שבו שילוב לינארי של המשתנים food[j]
(בהגדרה הבאה) גדול מ-nutrients[i][1]
או שווה לו.
המקדמים של הביטוי הלינארי מוגדרים באמצעות השיטה SetCoefficient
, באופן הבא: SetCoefficient(food[j], data[j][i+3]
פעולה זו מגדירה את המקדם של food[j]
ל-data[j][i+3]
.
הקוד מגדיר את המגבלות שפורטו בסעיף (1) שלמעלה.
יצירת היעד
הקוד הבא מגדיר את פונקציית היעד של הבעיה.
Python
# Objective function: Minimize the sum of (price-normalized) foods. objective = solver.Objective() for food in foods: objective.SetCoefficient(food, 1) objective.SetMinimization()
C++
MPObjective* const objective = solver->MutableObjective(); for (size_t i = 0; i < data.size(); ++i) { objective->SetCoefficient(foods[i], 1); } objective->SetMinimization();
Java
MPObjective objective = solver.objective(); for (int i = 0; i < data.size(); ++i) { objective.setCoefficient(foods.get(i), 1); } objective.setMinimization();
C#
Objective objective = solver.Objective(); for (int i = 0; i < data.Length; ++i) { objective.SetCoefficient(foods[i], 1); } objective.SetMinimization();
פונקציית היעד היא העלות הכוללת של המזון, שהיא סכום המשתנים food[i]
.
השיטה SetCoefficient
מגדירה את המקדמים של פונקציית היעד, שכולם 1
במקרה הזה.
לבסוף, ב-SetMinimization
מצהירים שזו בעיית מזעור.
מזמינים את הפותר
הקוד הבא מפעיל את הפותר.
Python
print(f"Solving with {solver.SolverVersion()}") status = solver.Solve()
C++
const MPSolver::ResultStatus result_status = solver->Solve();
Java
final MPSolver.ResultStatus resultStatus = solver.solve();
C#
Solver.ResultStatus resultStatus = solver.Solve();
Glop פותר את הבעיה במחשב טיפוסי תוך פחות מ-300 אלפיות השנייה:
הצגת הפתרון
הקוד הבא מציג את הפתרון.
Python
# Check that the problem has an optimal solution. if status != solver.OPTIMAL: print("The problem does not have an optimal solution!") if status == solver.FEASIBLE: print("A potentially suboptimal solution was found.") else: print("The solver could not solve the problem.") exit(1) # Display the amounts (in dollars) to purchase of each food. nutrients_result = [0] * len(nutrients) print("\nAnnual Foods:") for i, food in enumerate(foods): if food.solution_value() > 0.0: print("{}: ${}".format(data[i][0], 365.0 * food.solution_value())) for j, _ in enumerate(nutrients): nutrients_result[j] += data[i][j + 3] * food.solution_value() print("\nOptimal annual price: ${:.4f}".format(365.0 * objective.Value())) print("\nNutrients per day:") for i, nutrient in enumerate(nutrients): print( "{}: {:.2f} (min {})".format(nutrient[0], nutrients_result[i], nutrient[1]) )
C++
// Check that the problem has an optimal solution. if (result_status != MPSolver::OPTIMAL) { LOG(INFO) << "The problem does not have an optimal solution!"; if (result_status == MPSolver::FEASIBLE) { LOG(INFO) << "A potentially suboptimal solution was found"; } else { LOG(INFO) << "The solver could not solve the problem."; return; } } std::vector<double> nutrients_result(nutrients.size()); LOG(INFO) << ""; LOG(INFO) << "Annual Foods:"; for (std::size_t i = 0; i < data.size(); ++i) { if (foods[i]->solution_value() > 0.0) { LOG(INFO) << data[i].name << ": $" << std::to_string(365. * foods[i]->solution_value()); for (std::size_t j = 0; j < nutrients.size(); ++j) { nutrients_result[j] += data[i].nutrients[j] * foods[i]->solution_value(); } } } LOG(INFO) << ""; LOG(INFO) << "Optimal annual price: $" << std::to_string(365. * objective->Value()); LOG(INFO) << ""; LOG(INFO) << "Nutrients per day:"; for (std::size_t i = 0; i < nutrients.size(); ++i) { LOG(INFO) << nutrients[i].first << ": " << std::to_string(nutrients_result[i]) << " (min " << std::to_string(nutrients[i].second) << ")"; }
Java
// Check that the problem has an optimal solution. if (resultStatus != MPSolver.ResultStatus.OPTIMAL) { System.err.println("The problem does not have an optimal solution!"); if (resultStatus == MPSolver.ResultStatus.FEASIBLE) { System.err.println("A potentially suboptimal solution was found."); } else { System.err.println("The solver could not solve the problem."); return; } } // Display the amounts (in dollars) to purchase of each food. double[] nutrientsResult = new double[nutrients.size()]; System.out.println("\nAnnual Foods:"); for (int i = 0; i < foods.size(); ++i) { if (foods.get(i).solutionValue() > 0.0) { System.out.println((String) data.get(i)[0] + ": $" + 365 * foods.get(i).solutionValue()); for (int j = 0; j < nutrients.size(); ++j) { nutrientsResult[j] += ((double[]) data.get(i)[3])[j] * foods.get(i).solutionValue(); } } } System.out.println("\nOptimal annual price: $" + 365 * objective.value()); System.out.println("\nNutrients per day:"); for (int i = 0; i < nutrients.size(); ++i) { System.out.println( nutrients.get(i)[0] + ": " + nutrientsResult[i] + " (min " + nutrients.get(i)[1] + ")"); }
C#
// Check that the problem has an optimal solution. if (resultStatus != Solver.ResultStatus.OPTIMAL) { Console.WriteLine("The problem does not have an optimal solution!"); if (resultStatus == Solver.ResultStatus.FEASIBLE) { Console.WriteLine("A potentially suboptimal solution was found."); } else { Console.WriteLine("The solver could not solve the problem."); return; } } // Display the amounts (in dollars) to purchase of each food. double[] nutrientsResult = new double[nutrients.Length]; Console.WriteLine("\nAnnual Foods:"); for (int i = 0; i < foods.Count; ++i) { if (foods[i].SolutionValue() > 0.0) { Console.WriteLine($"{data[i].Name}: ${365 * foods[i].SolutionValue():N2}"); for (int j = 0; j < nutrients.Length; ++j) { nutrientsResult[j] += data[i].Nutrients[j] * foods[i].SolutionValue(); } } } Console.WriteLine($"\nOptimal annual price: ${365 * objective.Value():N2}"); Console.WriteLine("\nNutrients per day:"); for (int i = 0; i < nutrients.Length; ++i) { Console.WriteLine($"{nutrients[i].Name}: {nutrientsResult[i]:N2} (min {nutrients[i].Value})"); }
הנה הפלט של התוכנה.
make rpy_stigler_diet "/usr/bin/python3.11" ortools/linear_solver/samples/stigler_diet.py Number of variables = 77 Number of constraints = 9 Annual Foods: Wheat Flour (Enriched): $10.774457511918223 Liver (Beef): $0.6907834111074193 Cabbage: $4.093268864842877 Spinach: $1.8277960703546996 Navy Beans, Dried: $22.275425687243036 Optimal annual price: $39.6617 Nutrients per day: Calories (kcal): 3.00 (min 3) Protein (g): 147.41 (min 70) Calcium (g): 0.80 (min 0.8) Iron (mg): 60.47 (min 12) Vitamin A (KIU): 5.00 (min 5) Vitamin B1 (mg): 4.12 (min 1.8) Vitamin B2 (mg): 2.70 (min 2.7) Niacin (mg): 27.32 (min 18) Vitamin C (mg): 75.00 (min 75) Advanced usage: Problem solved in 1 milliseconds Problem solved in 14 iterations
השלמת הקוד של התוכנית
הקוד המלא של תוכנית הדיאטה של Stigler מופיע בהמשך.
Python
"""The Stigler diet problem. A description of the problem can be found here: https://en.wikipedia.org/wiki/Stigler_diet. """ from ortools.linear_solver import pywraplp def main(): """Entry point of the program.""" # Instantiate the data problem. # Nutrient minimums. nutrients = [ ["Calories (kcal)", 3], ["Protein (g)", 70], ["Calcium (g)", 0.8], ["Iron (mg)", 12], ["Vitamin A (KIU)", 5], ["Vitamin B1 (mg)", 1.8], ["Vitamin B2 (mg)", 2.7], ["Niacin (mg)", 18], ["Vitamin C (mg)", 75], ] # Commodity, Unit, 1939 price (cents), Calories (kcal), Protein (g), # Calcium (g), Iron (mg), Vitamin A (KIU), Vitamin B1 (mg), Vitamin B2 (mg), # Niacin (mg), Vitamin C (mg) data = [ # fmt: off ['Wheat Flour (Enriched)', '10 lb.', 36, 44.7, 1411, 2, 365, 0, 55.4, 33.3, 441, 0], ['Macaroni', '1 lb.', 14.1, 11.6, 418, 0.7, 54, 0, 3.2, 1.9, 68, 0], ['Wheat Cereal (Enriched)', '28 oz.', 24.2, 11.8, 377, 14.4, 175, 0, 14.4, 8.8, 114, 0], ['Corn Flakes', '8 oz.', 7.1, 11.4, 252, 0.1, 56, 0, 13.5, 2.3, 68, 0], ['Corn Meal', '1 lb.', 4.6, 36.0, 897, 1.7, 99, 30.9, 17.4, 7.9, 106, 0], ['Hominy Grits', '24 oz.', 8.5, 28.6, 680, 0.8, 80, 0, 10.6, 1.6, 110, 0], ['Rice', '1 lb.', 7.5, 21.2, 460, 0.6, 41, 0, 2, 4.8, 60, 0], ['Rolled Oats', '1 lb.', 7.1, 25.3, 907, 5.1, 341, 0, 37.1, 8.9, 64, 0], ['White Bread (Enriched)', '1 lb.', 7.9, 15.0, 488, 2.5, 115, 0, 13.8, 8.5, 126, 0], ['Whole Wheat Bread', '1 lb.', 9.1, 12.2, 484, 2.7, 125, 0, 13.9, 6.4, 160, 0], ['Rye Bread', '1 lb.', 9.1, 12.4, 439, 1.1, 82, 0, 9.9, 3, 66, 0], ['Pound Cake', '1 lb.', 24.8, 8.0, 130, 0.4, 31, 18.9, 2.8, 3, 17, 0], ['Soda Crackers', '1 lb.', 15.1, 12.5, 288, 0.5, 50, 0, 0, 0, 0, 0], ['Milk', '1 qt.', 11, 6.1, 310, 10.5, 18, 16.8, 4, 16, 7, 177], ['Evaporated Milk (can)', '14.5 oz.', 6.7, 8.4, 422, 15.1, 9, 26, 3, 23.5, 11, 60], ['Butter', '1 lb.', 30.8, 10.8, 9, 0.2, 3, 44.2, 0, 0.2, 2, 0], ['Oleomargarine', '1 lb.', 16.1, 20.6, 17, 0.6, 6, 55.8, 0.2, 0, 0, 0], ['Eggs', '1 doz.', 32.6, 2.9, 238, 1.0, 52, 18.6, 2.8, 6.5, 1, 0], ['Cheese (Cheddar)', '1 lb.', 24.2, 7.4, 448, 16.4, 19, 28.1, 0.8, 10.3, 4, 0], ['Cream', '1/2 pt.', 14.1, 3.5, 49, 1.7, 3, 16.9, 0.6, 2.5, 0, 17], ['Peanut Butter', '1 lb.', 17.9, 15.7, 661, 1.0, 48, 0, 9.6, 8.1, 471, 0], ['Mayonnaise', '1/2 pt.', 16.7, 8.6, 18, 0.2, 8, 2.7, 0.4, 0.5, 0, 0], ['Crisco', '1 lb.', 20.3, 20.1, 0, 0, 0, 0, 0, 0, 0, 0], ['Lard', '1 lb.', 9.8, 41.7, 0, 0, 0, 0.2, 0, 0.5, 5, 0], ['Sirloin Steak', '1 lb.', 39.6, 2.9, 166, 0.1, 34, 0.2, 2.1, 2.9, 69, 0], ['Round Steak', '1 lb.', 36.4, 2.2, 214, 0.1, 32, 0.4, 2.5, 2.4, 87, 0], ['Rib Roast', '1 lb.', 29.2, 3.4, 213, 0.1, 33, 0, 0, 2, 0, 0], ['Chuck Roast', '1 lb.', 22.6, 3.6, 309, 0.2, 46, 0.4, 1, 4, 120, 0], ['Plate', '1 lb.', 14.6, 8.5, 404, 0.2, 62, 0, 0.9, 0, 0, 0], ['Liver (Beef)', '1 lb.', 26.8, 2.2, 333, 0.2, 139, 169.2, 6.4, 50.8, 316, 525], ['Leg of Lamb', '1 lb.', 27.6, 3.1, 245, 0.1, 20, 0, 2.8, 3.9, 86, 0], ['Lamb Chops (Rib)', '1 lb.', 36.6, 3.3, 140, 0.1, 15, 0, 1.7, 2.7, 54, 0], ['Pork Chops', '1 lb.', 30.7, 3.5, 196, 0.2, 30, 0, 17.4, 2.7, 60, 0], ['Pork Loin Roast', '1 lb.', 24.2, 4.4, 249, 0.3, 37, 0, 18.2, 3.6, 79, 0], ['Bacon', '1 lb.', 25.6, 10.4, 152, 0.2, 23, 0, 1.8, 1.8, 71, 0], ['Ham, smoked', '1 lb.', 27.4, 6.7, 212, 0.2, 31, 0, 9.9, 3.3, 50, 0], ['Salt Pork', '1 lb.', 16, 18.8, 164, 0.1, 26, 0, 1.4, 1.8, 0, 0], ['Roasting Chicken', '1 lb.', 30.3, 1.8, 184, 0.1, 30, 0.1, 0.9, 1.8, 68, 46], ['Veal Cutlets', '1 lb.', 42.3, 1.7, 156, 0.1, 24, 0, 1.4, 2.4, 57, 0], ['Salmon, Pink (can)', '16 oz.', 13, 5.8, 705, 6.8, 45, 3.5, 1, 4.9, 209, 0], ['Apples', '1 lb.', 4.4, 5.8, 27, 0.5, 36, 7.3, 3.6, 2.7, 5, 544], ['Bananas', '1 lb.', 6.1, 4.9, 60, 0.4, 30, 17.4, 2.5, 3.5, 28, 498], ['Lemons', '1 doz.', 26, 1.0, 21, 0.5, 14, 0, 0.5, 0, 4, 952], ['Oranges', '1 doz.', 30.9, 2.2, 40, 1.1, 18, 11.1, 3.6, 1.3, 10, 1998], ['Green Beans', '1 lb.', 7.1, 2.4, 138, 3.7, 80, 69, 4.3, 5.8, 37, 862], ['Cabbage', '1 lb.', 3.7, 2.6, 125, 4.0, 36, 7.2, 9, 4.5, 26, 5369], ['Carrots', '1 bunch', 4.7, 2.7, 73, 2.8, 43, 188.5, 6.1, 4.3, 89, 608], ['Celery', '1 stalk', 7.3, 0.9, 51, 3.0, 23, 0.9, 1.4, 1.4, 9, 313], ['Lettuce', '1 head', 8.2, 0.4, 27, 1.1, 22, 112.4, 1.8, 3.4, 11, 449], ['Onions', '1 lb.', 3.6, 5.8, 166, 3.8, 59, 16.6, 4.7, 5.9, 21, 1184], ['Potatoes', '15 lb.', 34, 14.3, 336, 1.8, 118, 6.7, 29.4, 7.1, 198, 2522], ['Spinach', '1 lb.', 8.1, 1.1, 106, 0, 138, 918.4, 5.7, 13.8, 33, 2755], ['Sweet Potatoes', '1 lb.', 5.1, 9.6, 138, 2.7, 54, 290.7, 8.4, 5.4, 83, 1912], ['Peaches (can)', 'No. 2 1/2', 16.8, 3.7, 20, 0.4, 10, 21.5, 0.5, 1, 31, 196], ['Pears (can)', 'No. 2 1/2', 20.4, 3.0, 8, 0.3, 8, 0.8, 0.8, 0.8, 5, 81], ['Pineapple (can)', 'No. 2 1/2', 21.3, 2.4, 16, 0.4, 8, 2, 2.8, 0.8, 7, 399], ['Asparagus (can)', 'No. 2', 27.7, 0.4, 33, 0.3, 12, 16.3, 1.4, 2.1, 17, 272], ['Green Beans (can)', 'No. 2', 10, 1.0, 54, 2, 65, 53.9, 1.6, 4.3, 32, 431], ['Pork and Beans (can)', '16 oz.', 7.1, 7.5, 364, 4, 134, 3.5, 8.3, 7.7, 56, 0], ['Corn (can)', 'No. 2', 10.4, 5.2, 136, 0.2, 16, 12, 1.6, 2.7, 42, 218], ['Peas (can)', 'No. 2', 13.8, 2.3, 136, 0.6, 45, 34.9, 4.9, 2.5, 37, 370], ['Tomatoes (can)', 'No. 2', 8.6, 1.3, 63, 0.7, 38, 53.2, 3.4, 2.5, 36, 1253], ['Tomato Soup (can)', '10 1/2 oz.', 7.6, 1.6, 71, 0.6, 43, 57.9, 3.5, 2.4, 67, 862], ['Peaches, Dried', '1 lb.', 15.7, 8.5, 87, 1.7, 173, 86.8, 1.2, 4.3, 55, 57], ['Prunes, Dried', '1 lb.', 9, 12.8, 99, 2.5, 154, 85.7, 3.9, 4.3, 65, 257], ['Raisins, Dried', '15 oz.', 9.4, 13.5, 104, 2.5, 136, 4.5, 6.3, 1.4, 24, 136], ['Peas, Dried', '1 lb.', 7.9, 20.0, 1367, 4.2, 345, 2.9, 28.7, 18.4, 162, 0], ['Lima Beans, Dried', '1 lb.', 8.9, 17.4, 1055, 3.7, 459, 5.1, 26.9, 38.2, 93, 0], ['Navy Beans, Dried', '1 lb.', 5.9, 26.9, 1691, 11.4, 792, 0, 38.4, 24.6, 217, 0], ['Coffee', '1 lb.', 22.4, 0, 0, 0, 0, 0, 4, 5.1, 50, 0], ['Tea', '1/4 lb.', 17.4, 0, 0, 0, 0, 0, 0, 2.3, 42, 0], ['Cocoa', '8 oz.', 8.6, 8.7, 237, 3, 72, 0, 2, 11.9, 40, 0], ['Chocolate', '8 oz.', 16.2, 8.0, 77, 1.3, 39, 0, 0.9, 3.4, 14, 0], ['Sugar', '10 lb.', 51.7, 34.9, 0, 0, 0, 0, 0, 0, 0, 0], ['Corn Syrup', '24 oz.', 13.7, 14.7, 0, 0.5, 74, 0, 0, 0, 5, 0], ['Molasses', '18 oz.', 13.6, 9.0, 0, 10.3, 244, 0, 1.9, 7.5, 146, 0], ['Strawberry Preserves', '1 lb.', 20.5, 6.4, 11, 0.4, 7, 0.2, 0.2, 0.4, 3, 0], # fmt: on ] # Instantiate a Glop solver and naming it. solver = pywraplp.Solver.CreateSolver("GLOP") if not solver: return # Declare an array to hold our variables. foods = [solver.NumVar(0.0, solver.infinity(), item[0]) for item in data] print("Number of variables =", solver.NumVariables()) # Create the constraints, one per nutrient. constraints = [] for i, nutrient in enumerate(nutrients): constraints.append(solver.Constraint(nutrient[1], solver.infinity())) for j, item in enumerate(data): constraints[i].SetCoefficient(foods[j], item[i + 3]) print("Number of constraints =", solver.NumConstraints()) # Objective function: Minimize the sum of (price-normalized) foods. objective = solver.Objective() for food in foods: objective.SetCoefficient(food, 1) objective.SetMinimization() print(f"Solving with {solver.SolverVersion()}") status = solver.Solve() # Check that the problem has an optimal solution. if status != solver.OPTIMAL: print("The problem does not have an optimal solution!") if status == solver.FEASIBLE: print("A potentially suboptimal solution was found.") else: print("The solver could not solve the problem.") exit(1) # Display the amounts (in dollars) to purchase of each food. nutrients_result = [0] * len(nutrients) print("\nAnnual Foods:") for i, food in enumerate(foods): if food.solution_value() > 0.0: print("{}: ${}".format(data[i][0], 365.0 * food.solution_value())) for j, _ in enumerate(nutrients): nutrients_result[j] += data[i][j + 3] * food.solution_value() print("\nOptimal annual price: ${:.4f}".format(365.0 * objective.Value())) print("\nNutrients per day:") for i, nutrient in enumerate(nutrients): print( "{}: {:.2f} (min {})".format(nutrient[0], nutrients_result[i], nutrient[1]) ) print("\nAdvanced usage:") print(f"Problem solved in {solver.wall_time():d} milliseconds") print(f"Problem solved in {solver.iterations():d} iterations") if __name__ == "__main__": main()
C++
// The Stigler diet problem. #include <array> #include <memory> #include <string> #include <utility> // std::pair #include <vector> #include "absl/flags/flag.h" #include "absl/log/flags.h" #include "ortools/base/init_google.h" #include "ortools/base/logging.h" #include "ortools/linear_solver/linear_solver.h" namespace operations_research { void StiglerDiet() { // Nutrient minimums. const std::vector<std::pair<std::string, double>> nutrients = { {"Calories (kcal)", 3.0}, {"Protein (g)", 70.0}, {"Calcium (g)", 0.8}, {"Iron (mg)", 12.0}, {"Vitamin A (kIU)", 5.0}, {"Vitamin B1 (mg)", 1.8}, {"Vitamin B2 (mg)", 2.7}, {"Niacin (mg)", 18.0}, {"Vitamin C (mg)", 75.0}}; struct Commodity { std::string name; //!< Commodity name std::string unit; //!< Unit double price; //!< 1939 price per unit (cents) //! Calories (kcal), //! Protein (g), //! Calcium (g), //! Iron (mg), //! Vitamin A (kIU), //! Vitamin B1 (mg), //! Vitamin B2 (mg), //! Niacin (mg), //! Vitamin C (mg) std::array<double, 9> nutrients; }; std::vector<Commodity> data = { {"Wheat Flour (Enriched)", "10 lb.", 36, {44.7, 1411, 2, 365, 0, 55.4, 33.3, 441, 0}}, {"Macaroni", "1 lb.", 14.1, {11.6, 418, 0.7, 54, 0, 3.2, 1.9, 68, 0}}, {"Wheat Cereal (Enriched)", "28 oz.", 24.2, {11.8, 377, 14.4, 175, 0, 14.4, 8.8, 114, 0}}, {"Corn Flakes", "8 oz.", 7.1, {11.4, 252, 0.1, 56, 0, 13.5, 2.3, 68, 0}}, {"Corn Meal", "1 lb.", 4.6, {36.0, 897, 1.7, 99, 30.9, 17.4, 7.9, 106, 0}}, {"Hominy Grits", "24 oz.", 8.5, {28.6, 680, 0.8, 80, 0, 10.6, 1.6, 110, 0}}, {"Rice", "1 lb.", 7.5, {21.2, 460, 0.6, 41, 0, 2, 4.8, 60, 0}}, {"Rolled Oats", "1 lb.", 7.1, {25.3, 907, 5.1, 341, 0, 37.1, 8.9, 64, 0}}, {"White Bread (Enriched)", "1 lb.", 7.9, {15.0, 488, 2.5, 115, 0, 13.8, 8.5, 126, 0}}, {"Whole Wheat Bread", "1 lb.", 9.1, {12.2, 484, 2.7, 125, 0, 13.9, 6.4, 160, 0}}, {"Rye Bread", "1 lb.", 9.1, {12.4, 439, 1.1, 82, 0, 9.9, 3, 66, 0}}, {"Pound Cake", "1 lb.", 24.8, {8.0, 130, 0.4, 31, 18.9, 2.8, 3, 17, 0}}, {"Soda Crackers", "1 lb.", 15.1, {12.5, 288, 0.5, 50, 0, 0, 0, 0, 0}}, {"Milk", "1 qt.", 11, {6.1, 310, 10.5, 18, 16.8, 4, 16, 7, 177}}, {"Evaporated Milk (can)", "14.5 oz.", 6.7, {8.4, 422, 15.1, 9, 26, 3, 23.5, 11, 60}}, {"Butter", "1 lb.", 30.8, {10.8, 9, 0.2, 3, 44.2, 0, 0.2, 2, 0}}, {"Oleomargarine", "1 lb.", 16.1, {20.6, 17, 0.6, 6, 55.8, 0.2, 0, 0, 0}}, {"Eggs", "1 doz.", 32.6, {2.9, 238, 1.0, 52, 18.6, 2.8, 6.5, 1, 0}}, {"Cheese (Cheddar)", "1 lb.", 24.2, {7.4, 448, 16.4, 19, 28.1, 0.8, 10.3, 4, 0}}, {"Cream", "1/2 pt.", 14.1, {3.5, 49, 1.7, 3, 16.9, 0.6, 2.5, 0, 17}}, {"Peanut Butter", "1 lb.", 17.9, {15.7, 661, 1.0, 48, 0, 9.6, 8.1, 471, 0}}, {"Mayonnaise", "1/2 pt.", 16.7, {8.6, 18, 0.2, 8, 2.7, 0.4, 0.5, 0, 0}}, {"Crisco", "1 lb.", 20.3, {20.1, 0, 0, 0, 0, 0, 0, 0, 0}}, {"Lard", "1 lb.", 9.8, {41.7, 0, 0, 0, 0.2, 0, 0.5, 5, 0}}, {"Sirloin Steak", "1 lb.", 39.6, {2.9, 166, 0.1, 34, 0.2, 2.1, 2.9, 69, 0}}, {"Round Steak", "1 lb.", 36.4, {2.2, 214, 0.1, 32, 0.4, 2.5, 2.4, 87, 0}}, {"Rib Roast", "1 lb.", 29.2, {3.4, 213, 0.1, 33, 0, 0, 2, 0, 0}}, {"Chuck Roast", "1 lb.", 22.6, {3.6, 309, 0.2, 46, 0.4, 1, 4, 120, 0}}, {"Plate", "1 lb.", 14.6, {8.5, 404, 0.2, 62, 0, 0.9, 0, 0, 0}}, {"Liver (Beef)", "1 lb.", 26.8, {2.2, 333, 0.2, 139, 169.2, 6.4, 50.8, 316, 525}}, {"Leg of Lamb", "1 lb.", 27.6, {3.1, 245, 0.1, 20, 0, 2.8, 3.9, 86, 0}}, {"Lamb Chops (Rib)", "1 lb.", 36.6, {3.3, 140, 0.1, 15, 0, 1.7, 2.7, 54, 0}}, {"Pork Chops", "1 lb.", 30.7, {3.5, 196, 0.2, 30, 0, 17.4, 2.7, 60, 0}}, {"Pork Loin Roast", "1 lb.", 24.2, {4.4, 249, 0.3, 37, 0, 18.2, 3.6, 79, 0}}, {"Bacon", "1 lb.", 25.6, {10.4, 152, 0.2, 23, 0, 1.8, 1.8, 71, 0}}, {"Ham, smoked", "1 lb.", 27.4, {6.7, 212, 0.2, 31, 0, 9.9, 3.3, 50, 0}}, {"Salt Pork", "1 lb.", 16, {18.8, 164, 0.1, 26, 0, 1.4, 1.8, 0, 0}}, {"Roasting Chicken", "1 lb.", 30.3, {1.8, 184, 0.1, 30, 0.1, 0.9, 1.8, 68, 46}}, {"Veal Cutlets", "1 lb.", 42.3, {1.7, 156, 0.1, 24, 0, 1.4, 2.4, 57, 0}}, {"Salmon, Pink (can)", "16 oz.", 13, {5.8, 705, 6.8, 45, 3.5, 1, 4.9, 209, 0}}, {"Apples", "1 lb.", 4.4, {5.8, 27, 0.5, 36, 7.3, 3.6, 2.7, 5, 544}}, {"Bananas", "1 lb.", 6.1, {4.9, 60, 0.4, 30, 17.4, 2.5, 3.5, 28, 498}}, {"Lemons", "1 doz.", 26, {1.0, 21, 0.5, 14, 0, 0.5, 0, 4, 952}}, {"Oranges", "1 doz.", 30.9, {2.2, 40, 1.1, 18, 11.1, 3.6, 1.3, 10, 1998}}, {"Green Beans", "1 lb.", 7.1, {2.4, 138, 3.7, 80, 69, 4.3, 5.8, 37, 862}}, {"Cabbage", "1 lb.", 3.7, {2.6, 125, 4.0, 36, 7.2, 9, 4.5, 26, 5369}}, {"Carrots", "1 bunch", 4.7, {2.7, 73, 2.8, 43, 188.5, 6.1, 4.3, 89, 608}}, {"Celery", "1 stalk", 7.3, {0.9, 51, 3.0, 23, 0.9, 1.4, 1.4, 9, 313}}, {"Lettuce", "1 head", 8.2, {0.4, 27, 1.1, 22, 112.4, 1.8, 3.4, 11, 449}}, {"Onions", "1 lb.", 3.6, {5.8, 166, 3.8, 59, 16.6, 4.7, 5.9, 21, 1184}}, {"Potatoes", "15 lb.", 34, {14.3, 336, 1.8, 118, 6.7, 29.4, 7.1, 198, 2522}}, {"Spinach", "1 lb.", 8.1, {1.1, 106, 0, 138, 918.4, 5.7, 13.8, 33, 2755}}, {"Sweet Potatoes", "1 lb.", 5.1, {9.6, 138, 2.7, 54, 290.7, 8.4, 5.4, 83, 1912}}, {"Peaches (can)", "No. 2 1/2", 16.8, {3.7, 20, 0.4, 10, 21.5, 0.5, 1, 31, 196}}, {"Pears (can)", "No. 2 1/2", 20.4, {3.0, 8, 0.3, 8, 0.8, 0.8, 0.8, 5, 81}}, {"Pineapple (can)", "No. 2 1/2", 21.3, {2.4, 16, 0.4, 8, 2, 2.8, 0.8, 7, 399}}, {"Asparagus (can)", "No. 2", 27.7, {0.4, 33, 0.3, 12, 16.3, 1.4, 2.1, 17, 272}}, {"Green Beans (can)", "No. 2", 10, {1.0, 54, 2, 65, 53.9, 1.6, 4.3, 32, 431}}, {"Pork and Beans (can)", "16 oz.", 7.1, {7.5, 364, 4, 134, 3.5, 8.3, 7.7, 56, 0}}, {"Corn (can)", "No. 2", 10.4, {5.2, 136, 0.2, 16, 12, 1.6, 2.7, 42, 218}}, {"Peas (can)", "No. 2", 13.8, {2.3, 136, 0.6, 45, 34.9, 4.9, 2.5, 37, 370}}, {"Tomatoes (can)", "No. 2", 8.6, {1.3, 63, 0.7, 38, 53.2, 3.4, 2.5, 36, 1253}}, {"Tomato Soup (can)", "10 1/2 oz.", 7.6, {1.6, 71, 0.6, 43, 57.9, 3.5, 2.4, 67, 862}}, {"Peaches, Dried", "1 lb.", 15.7, {8.5, 87, 1.7, 173, 86.8, 1.2, 4.3, 55, 57}}, {"Prunes, Dried", "1 lb.", 9, {12.8, 99, 2.5, 154, 85.7, 3.9, 4.3, 65, 257}}, {"Raisins, Dried", "15 oz.", 9.4, {13.5, 104, 2.5, 136, 4.5, 6.3, 1.4, 24, 136}}, {"Peas, Dried", "1 lb.", 7.9, {20.0, 1367, 4.2, 345, 2.9, 28.7, 18.4, 162, 0}}, {"Lima Beans, Dried", "1 lb.", 8.9, {17.4, 1055, 3.7, 459, 5.1, 26.9, 38.2, 93, 0}}, {"Navy Beans, Dried", "1 lb.", 5.9, {26.9, 1691, 11.4, 792, 0, 38.4, 24.6, 217, 0}}, {"Coffee", "1 lb.", 22.4, {0, 0, 0, 0, 0, 4, 5.1, 50, 0}}, {"Tea", "1/4 lb.", 17.4, {0, 0, 0, 0, 0, 0, 2.3, 42, 0}}, {"Cocoa", "8 oz.", 8.6, {8.7, 237, 3, 72, 0, 2, 11.9, 40, 0}}, {"Chocolate", "8 oz.", 16.2, {8.0, 77, 1.3, 39, 0, 0.9, 3.4, 14, 0}}, {"Sugar", "10 lb.", 51.7, {34.9, 0, 0, 0, 0, 0, 0, 0, 0}}, {"Corn Syrup", "24 oz.", 13.7, {14.7, 0, 0.5, 74, 0, 0, 0, 5, 0}}, {"Molasses", "18 oz.", 13.6, {9.0, 0, 10.3, 244, 0, 1.9, 7.5, 146, 0}}, {"Strawberry Preserves", "1 lb.", 20.5, {6.4, 11, 0.4, 7, 0.2, 0.2, 0.4, 3, 0}}}; // Create the linear solver with the GLOP backend. std::unique_ptr<MPSolver> solver(MPSolver::CreateSolver("GLOP")); std::vector<MPVariable*> foods; const double infinity = solver->infinity(); for (const Commodity& commodity : data) { foods.push_back(solver->MakeNumVar(0.0, infinity, commodity.name)); } LOG(INFO) << "Number of variables = " << solver->NumVariables(); // Create the constraints, one per nutrient. std::vector<MPConstraint*> constraints; for (std::size_t i = 0; i < nutrients.size(); ++i) { constraints.push_back( solver->MakeRowConstraint(nutrients[i].second, infinity)); for (std::size_t j = 0; j < data.size(); ++j) { constraints.back()->SetCoefficient(foods[j], data[j].nutrients[i]); } } LOG(INFO) << "Number of constraints = " << solver->NumConstraints(); MPObjective* const objective = solver->MutableObjective(); for (size_t i = 0; i < data.size(); ++i) { objective->SetCoefficient(foods[i], 1); } objective->SetMinimization(); const MPSolver::ResultStatus result_status = solver->Solve(); // Check that the problem has an optimal solution. if (result_status != MPSolver::OPTIMAL) { LOG(INFO) << "The problem does not have an optimal solution!"; if (result_status == MPSolver::FEASIBLE) { LOG(INFO) << "A potentially suboptimal solution was found"; } else { LOG(INFO) << "The solver could not solve the problem."; return; } } std::vector<double> nutrients_result(nutrients.size()); LOG(INFO) << ""; LOG(INFO) << "Annual Foods:"; for (std::size_t i = 0; i < data.size(); ++i) { if (foods[i]->solution_value() > 0.0) { LOG(INFO) << data[i].name << ": $" << std::to_string(365. * foods[i]->solution_value()); for (std::size_t j = 0; j < nutrients.size(); ++j) { nutrients_result[j] += data[i].nutrients[j] * foods[i]->solution_value(); } } } LOG(INFO) << ""; LOG(INFO) << "Optimal annual price: $" << std::to_string(365. * objective->Value()); LOG(INFO) << ""; LOG(INFO) << "Nutrients per day:"; for (std::size_t i = 0; i < nutrients.size(); ++i) { LOG(INFO) << nutrients[i].first << ": " << std::to_string(nutrients_result[i]) << " (min " << std::to_string(nutrients[i].second) << ")"; } LOG(INFO) << ""; LOG(INFO) << "Advanced usage:"; LOG(INFO) << "Problem solved in " << solver->wall_time() << " milliseconds"; LOG(INFO) << "Problem solved in " << solver->iterations() << " iterations"; } } // namespace operations_research int main(int argc, char** argv) { InitGoogle(argv[0], &argc, &argv, true); absl::SetFlag(&FLAGS_stderrthreshold, 0); operations_research::StiglerDiet(); return EXIT_SUCCESS; }
Java
// The Stigler diet problem. package com.google.ortools.linearsolver.samples; import com.google.ortools.Loader; import com.google.ortools.linearsolver.MPConstraint; import com.google.ortools.linearsolver.MPObjective; import com.google.ortools.linearsolver.MPSolver; import com.google.ortools.linearsolver.MPVariable; import java.util.ArrayList; import java.util.List; /** Stigler diet example. */ public final class StiglerDiet { public static void main(String[] args) { Loader.loadNativeLibraries(); // Nutrient minimums. List<Object[]> nutrients = new ArrayList<>(); nutrients.add(new Object[] {"Calories (kcal)", 3.0}); nutrients.add(new Object[] {"Protein (g)", 70.0}); nutrients.add(new Object[] {"Calcium (g)", 0.8}); nutrients.add(new Object[] {"Iron (mg)", 12.0}); nutrients.add(new Object[] {"Vitamin A (kIU)", 5.0}); nutrients.add(new Object[] {"Vitamin B1 (mg)", 1.8}); nutrients.add(new Object[] {"Vitamin B2 (mg)", 2.7}); nutrients.add(new Object[] {"Niacin (mg)", 18.0}); nutrients.add(new Object[] {"Vitamin C (mg)", 75.0}); List<Object[]> data = new ArrayList<>(); data.add(new Object[] {"Wheat Flour (Enriched)", "10 lb.", 36, new double[] {44.7, 1411, 2, 365, 0, 55.4, 33.3, 441, 0}}); data.add(new Object[] { "Macaroni", "1 lb.", 14.1, new double[] {11.6, 418, 0.7, 54, 0, 3.2, 1.9, 68, 0}}); data.add(new Object[] {"Wheat Cereal (Enriched)", "28 oz.", 24.2, new double[] {11.8, 377, 14.4, 175, 0, 14.4, 8.8, 114, 0}}); data.add(new Object[] { "Corn Flakes", "8 oz.", 7.1, new double[] {11.4, 252, 0.1, 56, 0, 13.5, 2.3, 68, 0}}); data.add(new Object[] { "Corn Meal", "1 lb.", 4.6, new double[] {36.0, 897, 1.7, 99, 30.9, 17.4, 7.9, 106, 0}}); data.add(new Object[] { "Hominy Grits", "24 oz.", 8.5, new double[] {28.6, 680, 0.8, 80, 0, 10.6, 1.6, 110, 0}}); data.add( new Object[] {"Rice", "1 lb.", 7.5, new double[] {21.2, 460, 0.6, 41, 0, 2, 4.8, 60, 0}}); data.add(new Object[] { "Rolled Oats", "1 lb.", 7.1, new double[] {25.3, 907, 5.1, 341, 0, 37.1, 8.9, 64, 0}}); data.add(new Object[] {"White Bread (Enriched)", "1 lb.", 7.9, new double[] {15.0, 488, 2.5, 115, 0, 13.8, 8.5, 126, 0}}); data.add(new Object[] {"Whole Wheat Bread", "1 lb.", 9.1, new double[] {12.2, 484, 2.7, 125, 0, 13.9, 6.4, 160, 0}}); data.add(new Object[] { "Rye Bread", "1 lb.", 9.1, new double[] {12.4, 439, 1.1, 82, 0, 9.9, 3, 66, 0}}); data.add(new Object[] { "Pound Cake", "1 lb.", 24.8, new double[] {8.0, 130, 0.4, 31, 18.9, 2.8, 3, 17, 0}}); data.add(new Object[] { "Soda Crackers", "1 lb.", 15.1, new double[] {12.5, 288, 0.5, 50, 0, 0, 0, 0, 0}}); data.add( new Object[] {"Milk", "1 qt.", 11, new double[] {6.1, 310, 10.5, 18, 16.8, 4, 16, 7, 177}}); data.add(new Object[] {"Evaporated Milk (can)", "14.5 oz.", 6.7, new double[] {8.4, 422, 15.1, 9, 26, 3, 23.5, 11, 60}}); data.add( new Object[] {"Butter", "1 lb.", 30.8, new double[] {10.8, 9, 0.2, 3, 44.2, 0, 0.2, 2, 0}}); data.add(new Object[] { "Oleomargarine", "1 lb.", 16.1, new double[] {20.6, 17, 0.6, 6, 55.8, 0.2, 0, 0, 0}}); data.add(new Object[] { "Eggs", "1 doz.", 32.6, new double[] {2.9, 238, 1.0, 52, 18.6, 2.8, 6.5, 1, 0}}); data.add(new Object[] {"Cheese (Cheddar)", "1 lb.", 24.2, new double[] {7.4, 448, 16.4, 19, 28.1, 0.8, 10.3, 4, 0}}); data.add(new Object[] { "Cream", "1/2 pt.", 14.1, new double[] {3.5, 49, 1.7, 3, 16.9, 0.6, 2.5, 0, 17}}); data.add(new Object[] { "Peanut Butter", "1 lb.", 17.9, new double[] {15.7, 661, 1.0, 48, 0, 9.6, 8.1, 471, 0}}); data.add(new Object[] { "Mayonnaise", "1/2 pt.", 16.7, new double[] {8.6, 18, 0.2, 8, 2.7, 0.4, 0.5, 0, 0}}); data.add(new Object[] {"Crisco", "1 lb.", 20.3, new double[] {20.1, 0, 0, 0, 0, 0, 0, 0, 0}}); data.add(new Object[] {"Lard", "1 lb.", 9.8, new double[] {41.7, 0, 0, 0, 0.2, 0, 0.5, 5, 0}}); data.add(new Object[] { "Sirloin Steak", "1 lb.", 39.6, new double[] {2.9, 166, 0.1, 34, 0.2, 2.1, 2.9, 69, 0}}); data.add(new Object[] { "Round Steak", "1 lb.", 36.4, new double[] {2.2, 214, 0.1, 32, 0.4, 2.5, 2.4, 87, 0}}); data.add( new Object[] {"Rib Roast", "1 lb.", 29.2, new double[] {3.4, 213, 0.1, 33, 0, 0, 2, 0, 0}}); data.add(new Object[] { "Chuck Roast", "1 lb.", 22.6, new double[] {3.6, 309, 0.2, 46, 0.4, 1, 4, 120, 0}}); data.add( new Object[] {"Plate", "1 lb.", 14.6, new double[] {8.5, 404, 0.2, 62, 0, 0.9, 0, 0, 0}}); data.add(new Object[] {"Liver (Beef)", "1 lb.", 26.8, new double[] {2.2, 333, 0.2, 139, 169.2, 6.4, 50.8, 316, 525}}); data.add(new Object[] { "Leg of Lamb", "1 lb.", 27.6, new double[] {3.1, 245, 0.1, 20, 0, 2.8, 3.9, 86, 0}}); data.add(new Object[] { "Lamb Chops (Rib)", "1 lb.", 36.6, new double[] {3.3, 140, 0.1, 15, 0, 1.7, 2.7, 54, 0}}); data.add(new Object[] { "Pork Chops", "1 lb.", 30.7, new double[] {3.5, 196, 0.2, 30, 0, 17.4, 2.7, 60, 0}}); data.add(new Object[] { "Pork Loin Roast", "1 lb.", 24.2, new double[] {4.4, 249, 0.3, 37, 0, 18.2, 3.6, 79, 0}}); data.add(new Object[] { "Bacon", "1 lb.", 25.6, new double[] {10.4, 152, 0.2, 23, 0, 1.8, 1.8, 71, 0}}); data.add(new Object[] { "Ham, smoked", "1 lb.", 27.4, new double[] {6.7, 212, 0.2, 31, 0, 9.9, 3.3, 50, 0}}); data.add(new Object[] { "Salt Pork", "1 lb.", 16, new double[] {18.8, 164, 0.1, 26, 0, 1.4, 1.8, 0, 0}}); data.add(new Object[] {"Roasting Chicken", "1 lb.", 30.3, new double[] {1.8, 184, 0.1, 30, 0.1, 0.9, 1.8, 68, 46}}); data.add(new Object[] { "Veal Cutlets", "1 lb.", 42.3, new double[] {1.7, 156, 0.1, 24, 0, 1.4, 2.4, 57, 0}}); data.add(new Object[] { "Salmon, Pink (can)", "16 oz.", 13, new double[] {5.8, 705, 6.8, 45, 3.5, 1, 4.9, 209, 0}}); data.add(new Object[] { "Apples", "1 lb.", 4.4, new double[] {5.8, 27, 0.5, 36, 7.3, 3.6, 2.7, 5, 544}}); data.add(new Object[] { "Bananas", "1 lb.", 6.1, new double[] {4.9, 60, 0.4, 30, 17.4, 2.5, 3.5, 28, 498}}); data.add( new Object[] {"Lemons", "1 doz.", 26, new double[] {1.0, 21, 0.5, 14, 0, 0.5, 0, 4, 952}}); data.add(new Object[] { "Oranges", "1 doz.", 30.9, new double[] {2.2, 40, 1.1, 18, 11.1, 3.6, 1.3, 10, 1998}}); data.add(new Object[] { "Green Beans", "1 lb.", 7.1, new double[] {2.4, 138, 3.7, 80, 69, 4.3, 5.8, 37, 862}}); data.add(new Object[] { "Cabbage", "1 lb.", 3.7, new double[] {2.6, 125, 4.0, 36, 7.2, 9, 4.5, 26, 5369}}); data.add(new Object[] { "Carrots", "1 bunch", 4.7, new double[] {2.7, 73, 2.8, 43, 188.5, 6.1, 4.3, 89, 608}}); data.add(new Object[] { "Celery", "1 stalk", 7.3, new double[] {0.9, 51, 3.0, 23, 0.9, 1.4, 1.4, 9, 313}}); data.add(new Object[] { "Lettuce", "1 head", 8.2, new double[] {0.4, 27, 1.1, 22, 112.4, 1.8, 3.4, 11, 449}}); data.add(new Object[] { "Onions", "1 lb.", 3.6, new double[] {5.8, 166, 3.8, 59, 16.6, 4.7, 5.9, 21, 1184}}); data.add(new Object[] { "Potatoes", "15 lb.", 34, new double[] {14.3, 336, 1.8, 118, 6.7, 29.4, 7.1, 198, 2522}}); data.add(new Object[] { "Spinach", "1 lb.", 8.1, new double[] {1.1, 106, 0, 138, 918.4, 5.7, 13.8, 33, 2755}}); data.add(new Object[] {"Sweet Potatoes", "1 lb.", 5.1, new double[] {9.6, 138, 2.7, 54, 290.7, 8.4, 5.4, 83, 1912}}); data.add(new Object[] {"Peaches (can)", "No. 2 1/2", 16.8, new double[] {3.7, 20, 0.4, 10, 21.5, 0.5, 1, 31, 196}}); data.add(new Object[] { "Pears (can)", "No. 2 1/2", 20.4, new double[] {3.0, 8, 0.3, 8, 0.8, 0.8, 0.8, 5, 81}}); data.add(new Object[] { "Pineapple (can)", "No. 2 1/2", 21.3, new double[] {2.4, 16, 0.4, 8, 2, 2.8, 0.8, 7, 399}}); data.add(new Object[] {"Asparagus (can)", "No. 2", 27.7, new double[] {0.4, 33, 0.3, 12, 16.3, 1.4, 2.1, 17, 272}}); data.add(new Object[] { "Green Beans (can)", "No. 2", 10, new double[] {1.0, 54, 2, 65, 53.9, 1.6, 4.3, 32, 431}}); data.add(new Object[] {"Pork and Beans (can)", "16 oz.", 7.1, new double[] {7.5, 364, 4, 134, 3.5, 8.3, 7.7, 56, 0}}); data.add(new Object[] { "Corn (can)", "No. 2", 10.4, new double[] {5.2, 136, 0.2, 16, 12, 1.6, 2.7, 42, 218}}); data.add(new Object[] { "Peas (can)", "No. 2", 13.8, new double[] {2.3, 136, 0.6, 45, 34.9, 4.9, 2.5, 37, 370}}); data.add(new Object[] { "Tomatoes (can)", "No. 2", 8.6, new double[] {1.3, 63, 0.7, 38, 53.2, 3.4, 2.5, 36, 1253}}); data.add(new Object[] {"Tomato Soup (can)", "10 1/2 oz.", 7.6, new double[] {1.6, 71, 0.6, 43, 57.9, 3.5, 2.4, 67, 862}}); data.add(new Object[] { "Peaches, Dried", "1 lb.", 15.7, new double[] {8.5, 87, 1.7, 173, 86.8, 1.2, 4.3, 55, 57}}); data.add(new Object[] { "Prunes, Dried", "1 lb.", 9, new double[] {12.8, 99, 2.5, 154, 85.7, 3.9, 4.3, 65, 257}}); data.add(new Object[] {"Raisins, Dried", "15 oz.", 9.4, new double[] {13.5, 104, 2.5, 136, 4.5, 6.3, 1.4, 24, 136}}); data.add(new Object[] { "Peas, Dried", "1 lb.", 7.9, new double[] {20.0, 1367, 4.2, 345, 2.9, 28.7, 18.4, 162, 0}}); data.add(new Object[] {"Lima Beans, Dried", "1 lb.", 8.9, new double[] {17.4, 1055, 3.7, 459, 5.1, 26.9, 38.2, 93, 0}}); data.add(new Object[] {"Navy Beans, Dried", "1 lb.", 5.9, new double[] {26.9, 1691, 11.4, 792, 0, 38.4, 24.6, 217, 0}}); data.add(new Object[] {"Coffee", "1 lb.", 22.4, new double[] {0, 0, 0, 0, 0, 4, 5.1, 50, 0}}); data.add(new Object[] {"Tea", "1/4 lb.", 17.4, new double[] {0, 0, 0, 0, 0, 0, 2.3, 42, 0}}); data.add( new Object[] {"Cocoa", "8 oz.", 8.6, new double[] {8.7, 237, 3, 72, 0, 2, 11.9, 40, 0}}); data.add(new Object[] { "Chocolate", "8 oz.", 16.2, new double[] {8.0, 77, 1.3, 39, 0, 0.9, 3.4, 14, 0}}); data.add(new Object[] {"Sugar", "10 lb.", 51.7, new double[] {34.9, 0, 0, 0, 0, 0, 0, 0, 0}}); data.add(new Object[] { "Corn Syrup", "24 oz.", 13.7, new double[] {14.7, 0, 0.5, 74, 0, 0, 0, 5, 0}}); data.add(new Object[] { "Molasses", "18 oz.", 13.6, new double[] {9.0, 0, 10.3, 244, 0, 1.9, 7.5, 146, 0}}); data.add(new Object[] {"Strawberry Preserves", "1 lb.", 20.5, new double[] {6.4, 11, 0.4, 7, 0.2, 0.2, 0.4, 3, 0}}); // Create the linear solver with the GLOP backend. MPSolver solver = MPSolver.createSolver("GLOP"); if (solver == null) { System.out.println("Could not create solver GLOP"); return; } double infinity = java.lang.Double.POSITIVE_INFINITY; List<MPVariable> foods = new ArrayList<>(); for (int i = 0; i < data.size(); ++i) { foods.add(solver.makeNumVar(0.0, infinity, (String) data.get(i)[0])); } System.out.println("Number of variables = " + solver.numVariables()); MPConstraint[] constraints = new MPConstraint[nutrients.size()]; for (int i = 0; i < nutrients.size(); ++i) { constraints[i] = solver.makeConstraint( (double) nutrients.get(i)[1], infinity, (String) nutrients.get(i)[0]); for (int j = 0; j < data.size(); ++j) { constraints[i].setCoefficient(foods.get(j), ((double[]) data.get(j)[3])[i]); } // constraints.add(constraint); } System.out.println("Number of constraints = " + solver.numConstraints()); MPObjective objective = solver.objective(); for (int i = 0; i < data.size(); ++i) { objective.setCoefficient(foods.get(i), 1); } objective.setMinimization(); final MPSolver.ResultStatus resultStatus = solver.solve(); // Check that the problem has an optimal solution. if (resultStatus != MPSolver.ResultStatus.OPTIMAL) { System.err.println("The problem does not have an optimal solution!"); if (resultStatus == MPSolver.ResultStatus.FEASIBLE) { System.err.println("A potentially suboptimal solution was found."); } else { System.err.println("The solver could not solve the problem."); return; } } // Display the amounts (in dollars) to purchase of each food. double[] nutrientsResult = new double[nutrients.size()]; System.out.println("\nAnnual Foods:"); for (int i = 0; i < foods.size(); ++i) { if (foods.get(i).solutionValue() > 0.0) { System.out.println((String) data.get(i)[0] + ": $" + 365 * foods.get(i).solutionValue()); for (int j = 0; j < nutrients.size(); ++j) { nutrientsResult[j] += ((double[]) data.get(i)[3])[j] * foods.get(i).solutionValue(); } } } System.out.println("\nOptimal annual price: $" + 365 * objective.value()); System.out.println("\nNutrients per day:"); for (int i = 0; i < nutrients.size(); ++i) { System.out.println( nutrients.get(i)[0] + ": " + nutrientsResult[i] + " (min " + nutrients.get(i)[1] + ")"); } System.out.println("\nAdvanced usage:"); System.out.println("Problem solved in " + solver.wallTime() + " milliseconds"); System.out.println("Problem solved in " + solver.iterations() + " iterations"); } private StiglerDiet() {} }
C#
// The Stigler diet problem. using System; using System.Collections.Generic; using Google.OrTools.LinearSolver; public class StiglerDiet { static void Main() { // Nutrient minimums. (String Name, double Value)[] nutrients = new[] { ("Calories (kcal)", 3.0), ("Protein (g)", 70.0), ("Calcium (g)", 0.8), ("Iron (mg)", 12.0), ("Vitamin A (kIU)", 5.0), ("Vitamin B1 (mg)", 1.8), ("Vitamin B2 (mg)", 2.7), ("Niacin (mg)", 18.0), ("Vitamin C (mg)", 75.0) }; (String Name, String Unit, double Price, double[] Nutrients)[] data = new[] { ("Wheat Flour (Enriched)", "10 lb.", 36, new double[] { 44.7, 1411, 2, 365, 0, 55.4, 33.3, 441, 0 }), ("Macaroni", "1 lb.", 14.1, new double[] { 11.6, 418, 0.7, 54, 0, 3.2, 1.9, 68, 0 }), ("Wheat Cereal (Enriched)", "28 oz.", 24.2, new double[] { 11.8, 377, 14.4, 175, 0, 14.4, 8.8, 114, 0 }), ("Corn Flakes", "8 oz.", 7.1, new double[] { 11.4, 252, 0.1, 56, 0, 13.5, 2.3, 68, 0 }), ("Corn Meal", "1 lb.", 4.6, new double[] { 36.0, 897, 1.7, 99, 30.9, 17.4, 7.9, 106, 0 }), ("Hominy Grits", "24 oz.", 8.5, new double[] { 28.6, 680, 0.8, 80, 0, 10.6, 1.6, 110, 0 }), ("Rice", "1 lb.", 7.5, new double[] { 21.2, 460, 0.6, 41, 0, 2, 4.8, 60, 0 }), ("Rolled Oats", "1 lb.", 7.1, new double[] { 25.3, 907, 5.1, 341, 0, 37.1, 8.9, 64, 0 }), ("White Bread (Enriched)", "1 lb.", 7.9, new double[] { 15.0, 488, 2.5, 115, 0, 13.8, 8.5, 126, 0 }), ("Whole Wheat Bread", "1 lb.", 9.1, new double[] { 12.2, 484, 2.7, 125, 0, 13.9, 6.4, 160, 0 }), ("Rye Bread", "1 lb.", 9.1, new double[] { 12.4, 439, 1.1, 82, 0, 9.9, 3, 66, 0 }), ("Pound Cake", "1 lb.", 24.8, new double[] { 8.0, 130, 0.4, 31, 18.9, 2.8, 3, 17, 0 }), ("Soda Crackers", "1 lb.", 15.1, new double[] { 12.5, 288, 0.5, 50, 0, 0, 0, 0, 0 }), ("Milk", "1 qt.", 11, new double[] { 6.1, 310, 10.5, 18, 16.8, 4, 16, 7, 177 }), ("Evaporated Milk (can)", "14.5 oz.", 6.7, new double[] { 8.4, 422, 15.1, 9, 26, 3, 23.5, 11, 60 }), ("Butter", "1 lb.", 30.8, new double[] { 10.8, 9, 0.2, 3, 44.2, 0, 0.2, 2, 0 }), ("Oleomargarine", "1 lb.", 16.1, new double[] { 20.6, 17, 0.6, 6, 55.8, 0.2, 0, 0, 0 }), ("Eggs", "1 doz.", 32.6, new double[] { 2.9, 238, 1.0, 52, 18.6, 2.8, 6.5, 1, 0 }), ("Cheese (Cheddar)", "1 lb.", 24.2, new double[] { 7.4, 448, 16.4, 19, 28.1, 0.8, 10.3, 4, 0 }), ("Cream", "1/2 pt.", 14.1, new double[] { 3.5, 49, 1.7, 3, 16.9, 0.6, 2.5, 0, 17 }), ("Peanut Butter", "1 lb.", 17.9, new double[] { 15.7, 661, 1.0, 48, 0, 9.6, 8.1, 471, 0 }), ("Mayonnaise", "1/2 pt.", 16.7, new double[] { 8.6, 18, 0.2, 8, 2.7, 0.4, 0.5, 0, 0 }), ("Crisco", "1 lb.", 20.3, new double[] { 20.1, 0, 0, 0, 0, 0, 0, 0, 0 }), ("Lard", "1 lb.", 9.8, new double[] { 41.7, 0, 0, 0, 0.2, 0, 0.5, 5, 0 }), ("Sirloin Steak", "1 lb.", 39.6, new double[] { 2.9, 166, 0.1, 34, 0.2, 2.1, 2.9, 69, 0 }), ("Round Steak", "1 lb.", 36.4, new double[] { 2.2, 214, 0.1, 32, 0.4, 2.5, 2.4, 87, 0 }), ("Rib Roast", "1 lb.", 29.2, new double[] { 3.4, 213, 0.1, 33, 0, 0, 2, 0, 0 }), ("Chuck Roast", "1 lb.", 22.6, new double[] { 3.6, 309, 0.2, 46, 0.4, 1, 4, 120, 0 }), ("Plate", "1 lb.", 14.6, new double[] { 8.5, 404, 0.2, 62, 0, 0.9, 0, 0, 0 }), ("Liver (Beef)", "1 lb.", 26.8, new double[] { 2.2, 333, 0.2, 139, 169.2, 6.4, 50.8, 316, 525 }), ("Leg of Lamb", "1 lb.", 27.6, new double[] { 3.1, 245, 0.1, 20, 0, 2.8, 3.9, 86, 0 }), ("Lamb Chops (Rib)", "1 lb.", 36.6, new double[] { 3.3, 140, 0.1, 15, 0, 1.7, 2.7, 54, 0 }), ("Pork Chops", "1 lb.", 30.7, new double[] { 3.5, 196, 0.2, 30, 0, 17.4, 2.7, 60, 0 }), ("Pork Loin Roast", "1 lb.", 24.2, new double[] { 4.4, 249, 0.3, 37, 0, 18.2, 3.6, 79, 0 }), ("Bacon", "1 lb.", 25.6, new double[] { 10.4, 152, 0.2, 23, 0, 1.8, 1.8, 71, 0 }), ("Ham, smoked", "1 lb.", 27.4, new double[] { 6.7, 212, 0.2, 31, 0, 9.9, 3.3, 50, 0 }), ("Salt Pork", "1 lb.", 16, new double[] { 18.8, 164, 0.1, 26, 0, 1.4, 1.8, 0, 0 }), ("Roasting Chicken", "1 lb.", 30.3, new double[] { 1.8, 184, 0.1, 30, 0.1, 0.9, 1.8, 68, 46 }), ("Veal Cutlets", "1 lb.", 42.3, new double[] { 1.7, 156, 0.1, 24, 0, 1.4, 2.4, 57, 0 }), ("Salmon, Pink (can)", "16 oz.", 13, new double[] { 5.8, 705, 6.8, 45, 3.5, 1, 4.9, 209, 0 }), ("Apples", "1 lb.", 4.4, new double[] { 5.8, 27, 0.5, 36, 7.3, 3.6, 2.7, 5, 544 }), ("Bananas", "1 lb.", 6.1, new double[] { 4.9, 60, 0.4, 30, 17.4, 2.5, 3.5, 28, 498 }), ("Lemons", "1 doz.", 26, new double[] { 1.0, 21, 0.5, 14, 0, 0.5, 0, 4, 952 }), ("Oranges", "1 doz.", 30.9, new double[] { 2.2, 40, 1.1, 18, 11.1, 3.6, 1.3, 10, 1998 }), ("Green Beans", "1 lb.", 7.1, new double[] { 2.4, 138, 3.7, 80, 69, 4.3, 5.8, 37, 862 }), ("Cabbage", "1 lb.", 3.7, new double[] { 2.6, 125, 4.0, 36, 7.2, 9, 4.5, 26, 5369 }), ("Carrots", "1 bunch", 4.7, new double[] { 2.7, 73, 2.8, 43, 188.5, 6.1, 4.3, 89, 608 }), ("Celery", "1 stalk", 7.3, new double[] { 0.9, 51, 3.0, 23, 0.9, 1.4, 1.4, 9, 313 }), ("Lettuce", "1 head", 8.2, new double[] { 0.4, 27, 1.1, 22, 112.4, 1.8, 3.4, 11, 449 }), ("Onions", "1 lb.", 3.6, new double[] { 5.8, 166, 3.8, 59, 16.6, 4.7, 5.9, 21, 1184 }), ("Potatoes", "15 lb.", 34, new double[] { 14.3, 336, 1.8, 118, 6.7, 29.4, 7.1, 198, 2522 }), ("Spinach", "1 lb.", 8.1, new double[] { 1.1, 106, 0, 138, 918.4, 5.7, 13.8, 33, 2755 }), ("Sweet Potatoes", "1 lb.", 5.1, new double[] { 9.6, 138, 2.7, 54, 290.7, 8.4, 5.4, 83, 1912 }), ("Peaches (can)", "No. 2 1/2", 16.8, new double[] { 3.7, 20, 0.4, 10, 21.5, 0.5, 1, 31, 196 }), ("Pears (can)", "No. 2 1/2", 20.4, new double[] { 3.0, 8, 0.3, 8, 0.8, 0.8, 0.8, 5, 81 }), ("Pineapple (can)", "No. 2 1/2", 21.3, new double[] { 2.4, 16, 0.4, 8, 2, 2.8, 0.8, 7, 399 }), ("Asparagus (can)", "No. 2", 27.7, new double[] { 0.4, 33, 0.3, 12, 16.3, 1.4, 2.1, 17, 272 }), ("Green Beans (can)", "No. 2", 10, new double[] { 1.0, 54, 2, 65, 53.9, 1.6, 4.3, 32, 431 }), ("Pork and Beans (can)", "16 oz.", 7.1, new double[] { 7.5, 364, 4, 134, 3.5, 8.3, 7.7, 56, 0 }), ("Corn (can)", "No. 2", 10.4, new double[] { 5.2, 136, 0.2, 16, 12, 1.6, 2.7, 42, 218 }), ("Peas (can)", "No. 2", 13.8, new double[] { 2.3, 136, 0.6, 45, 34.9, 4.9, 2.5, 37, 370 }), ("Tomatoes (can)", "No. 2", 8.6, new double[] { 1.3, 63, 0.7, 38, 53.2, 3.4, 2.5, 36, 1253 }), ("Tomato Soup (can)", "10 1/2 oz.", 7.6, new double[] { 1.6, 71, 0.6, 43, 57.9, 3.5, 2.4, 67, 862 }), ("Peaches, Dried", "1 lb.", 15.7, new double[] { 8.5, 87, 1.7, 173, 86.8, 1.2, 4.3, 55, 57 }), ("Prunes, Dried", "1 lb.", 9, new double[] { 12.8, 99, 2.5, 154, 85.7, 3.9, 4.3, 65, 257 }), ("Raisins, Dried", "15 oz.", 9.4, new double[] { 13.5, 104, 2.5, 136, 4.5, 6.3, 1.4, 24, 136 }), ("Peas, Dried", "1 lb.", 7.9, new double[] { 20.0, 1367, 4.2, 345, 2.9, 28.7, 18.4, 162, 0 }), ("Lima Beans, Dried", "1 lb.", 8.9, new double[] { 17.4, 1055, 3.7, 459, 5.1, 26.9, 38.2, 93, 0 }), ("Navy Beans, Dried", "1 lb.", 5.9, new double[] { 26.9, 1691, 11.4, 792, 0, 38.4, 24.6, 217, 0 }), ("Coffee", "1 lb.", 22.4, new double[] { 0, 0, 0, 0, 0, 4, 5.1, 50, 0 }), ("Tea", "1/4 lb.", 17.4, new double[] { 0, 0, 0, 0, 0, 0, 2.3, 42, 0 }), ("Cocoa", "8 oz.", 8.6, new double[] { 8.7, 237, 3, 72, 0, 2, 11.9, 40, 0 }), ("Chocolate", "8 oz.", 16.2, new double[] { 8.0, 77, 1.3, 39, 0, 0.9, 3.4, 14, 0 }), ("Sugar", "10 lb.", 51.7, new double[] { 34.9, 0, 0, 0, 0, 0, 0, 0, 0 }), ("Corn Syrup", "24 oz.", 13.7, new double[] { 14.7, 0, 0.5, 74, 0, 0, 0, 5, 0 }), ("Molasses", "18 oz.", 13.6, new double[] { 9.0, 0, 10.3, 244, 0, 1.9, 7.5, 146, 0 }), ("Strawberry Preserves", "1 lb.", 20.5, new double[] { 6.4, 11, 0.4, 7, 0.2, 0.2, 0.4, 3, 0 }) }; // Create the linear solver with the GLOP backend. Solver solver = Solver.CreateSolver("GLOP"); if (solver is null) { return; } List<Variable> foods = new List<Variable>(); for (int i = 0; i < data.Length; ++i) { foods.Add(solver.MakeNumVar(0.0, double.PositiveInfinity, data[i].Name)); } Console.WriteLine($"Number of variables = {solver.NumVariables()}"); List<Constraint> constraints = new List<Constraint>(); for (int i = 0; i < nutrients.Length; ++i) { Constraint constraint = solver.MakeConstraint(nutrients[i].Value, double.PositiveInfinity, nutrients[i].Name); for (int j = 0; j < data.Length; ++j) { constraint.SetCoefficient(foods[j], data[j].Nutrients[i]); } constraints.Add(constraint); } Console.WriteLine($"Number of constraints = {solver.NumConstraints()}"); Objective objective = solver.Objective(); for (int i = 0; i < data.Length; ++i) { objective.SetCoefficient(foods[i], 1); } objective.SetMinimization(); Solver.ResultStatus resultStatus = solver.Solve(); // Check that the problem has an optimal solution. if (resultStatus != Solver.ResultStatus.OPTIMAL) { Console.WriteLine("The problem does not have an optimal solution!"); if (resultStatus == Solver.ResultStatus.FEASIBLE) { Console.WriteLine("A potentially suboptimal solution was found."); } else { Console.WriteLine("The solver could not solve the problem."); return; } } // Display the amounts (in dollars) to purchase of each food. double[] nutrientsResult = new double[nutrients.Length]; Console.WriteLine("\nAnnual Foods:"); for (int i = 0; i < foods.Count; ++i) { if (foods[i].SolutionValue() > 0.0) { Console.WriteLine($"{data[i].Name}: ${365 * foods[i].SolutionValue():N2}"); for (int j = 0; j < nutrients.Length; ++j) { nutrientsResult[j] += data[i].Nutrients[j] * foods[i].SolutionValue(); } } } Console.WriteLine($"\nOptimal annual price: ${365 * objective.Value():N2}"); Console.WriteLine("\nNutrients per day:"); for (int i = 0; i < nutrients.Length; ++i) { Console.WriteLine($"{nutrients[i].Name}: {nutrientsResult[i]:N2} (min {nutrients[i].Value})"); } Console.WriteLine("\nAdvanced usage:"); Console.WriteLine($"Problem solved in {solver.WallTime()} milliseconds"); Console.WriteLine($"Problem solved in {solver.Iterations()} iterations"); } }