本篇将介绍使用MindOpt来优化营养调配问题。(本例数据是假设的,决策值不能作为参考,仅为讲解决策算法用。)
营养调配问题的的目标是利用优化模型来设定每日饮食菜单,在满足各类营养的需求同时更能优化总成本.营养调配问题是可用线性优化来表达
- 决策 变量 为:以下食物调配多少量:起司汉堡 (Cheeseburger)、汉堡 (Hamburger)、火腿三明治 (Ham-sandwich)、鱼肉三民治 (Fish-sandwich)、鸡肉三民治 (Chicken-sandwich)、薯条 (Fries)、香肠比司吉 (Sausage biscuit)、低脂牛乳 (Low-fat milk)、和橙汁 (Orange juice);
- 约束 条件为:卡路里 (Cal.)、碳水化合物 (Carbo.)、蛋白质 (Portien)、维生素A/D (Vit. A/D)、铁 (Iron)和钙质 (Calc.)的每日摄取上/下限制,以及总量 (Volume) 限制;
- 目标 函数则为:总成本的最小化.
问题定义
问题类型
线性优化问题,我们用先用集合和参数标明后面变量的取值关联信息。
集合
参数
然后这个线性规划问题定义如下:
决策变量
目标函数
约束条件
总结下这个模型的数学公式为:
数据
- 为简化表达,以下以食物的简称替代.
食物 |
Cheese burger |
Ham sandwich |
Ham-burger |
Fish sandwich |
Chicken sandwich |
Fries |
Sausage biscuit |
Lowfat milk |
Orange juice |
简称 |
CB |
HS |
H |
FS |
CS |
F |
SB |
LM |
OJ |
- 每单位食物的成本价
食物 |
CB |
HS |
H |
FS |
CS |
F |
SB |
LM |
OJ |
成本 |
1.84 |
2.19 |
1.84 |
1.44 |
2.29 |
0.77 |
1.29 |
0.60 |
0.72 |
- 各营养的单日摄取上限和下限
营养 |
Cal |
Carbo |
Protein |
VitA |
VitC |
Calc |
Iron |
Volume |
下界 |
2000 |
355 |
55 |
100 |
100 |
100 |
100 |
-∞ |
上界 |
∞ |
375 |
∞ |
∞ |
∞ |
∞ |
∞ |
75 |
- 各种食物的营养含量与容积量
食物 |
CB |
HS |
H |
FS |
CS |
F |
SB |
LM |
OJ |
Cal |
510 |
370 |
500 |
370 |
400 |
220 |
345 |
110 |
80 |
Carbo |
34 |
55 |
42 |
38 |
42 |
26 |
27 |
12 |
20 |
Protein |
28 |
24 |
25 |
14 |
31 |
3 |
15 |
9 |
1 |
VitA |
15 |
15 |
6 |
2 |
8 |
0 |
4 |
10 |
2 |
VitC |
6 |
10 |
2 |
0 |
15 |
15 |
0 |
4 |
120 |
Calc |
30 |
20 |
25 |
15 |
15 |
0 |
20 |
30 |
2 |
Iron |
20 |
20 |
20 |
10 |
8 |
2 |
15 |
0 |
2 |
Volume |
4 |
7.5 |
3.5 |
5 |
7.3 |
2.6 |
4.1 |
8 |
12 |
使用MindOpt求解器的API
直接采用求解器的API,需要查阅API文档来理解API的意思,没有建模语言可读性高。请参阅https://solver.damo.alibaba.com/doc/html/API%20reference/API-python/index.html来查看PythonAPI的使用说明。
关于Python的例子,在达摩院 MindOpt优化中文社区中有Python的示例。这里是个LP的问题,我们可以参考:https://developer.aliyun.com/article/1090860?spm=a2c6h.26396819.creator-center.8.14fb3e182y9iZl
下面我们分三种方式描述在本平台环境中的运行方法:
方法1:cell中直接输入代码运行
示例:进入官网>进入线上平台>创建项目>输入代码
请运行下面cell中的代码,点击本窗口上面的播放△运行,或者摁shift+enter键运行:
# LP_2_diet.py """ /** * example_2_py1.py * Description * ----------- * * Linear optimization (diet problem). * * The goal is to select foods that satisfy daily nutritional requirements while minimizing the total cost. * The constraints in this problem limit the number of calories, the volume of good consumed, and the amount of * vitamins, protein, carbohydrates, calcium, and iron in the diet. * * Note * ---- * * The model below will be inputted in a row-wise order. * * Formulation * ----------- * * Minimize * Obj: 1.840000000 Cheeseburger + 2.190000000 HamSandwich + 1.840000000 Hamburger + 1.440000000 FishSandwich + * 2.290000000 ChickenSandwich + 0.770000000 Fries + 1.290000000 SausageBiscuit + 0.600000000 LowfatMilk + * 0.720000000 OrangeJuice * Subject To * Cal: 510 Cheeseburger + 370 HamSandwich + 500 Hamburger + 370 FishSandwich + * 400 ChickenSandwich + 220 Fries + 345 SausageBiscuit + 110 LowfatMilk + 80 OrangeJuice >= 2000 * Carbo: 34 Cheeseburger + 35 HamSandwich + 42 Hamburger + 38 FishSandwich + 42 ChickenSandwich + * 26 Fries + 27 SausageBiscuit + 12 LowfatMilk + 20 OrangeJuice <= 375 * Carbo_low: 34 Cheeseburger + 35 HamSandwich + 42 Hamburger + 38 FishSandwich + 42 ChickenSandwich + * 26 Fries + 27 SausageBiscuit + 12 LowfatMilk + 20 OrangeJuice >= 350 * Protein: 28 Cheeseburger + 24 HamSandwich + 25 Hamburger + 14 FishSandwich + 31 ChickenSandwich + * 3 Fries + 15 SausageBiscuit + 9 LowfatMilk + OrangeJuice >= 55 * VitA: 15 Cheeseburger + 15 HamSandwich + 6 Hamburger + 2 FishSandwich + 8 ChickenSandwich + * 4 SausageBiscuit + 10 LowfatMilk + 2 OrangeJuice >= 100 * VitC: 6 Cheeseburger + 10 HamSandwich + 2 Hamburger + 15 ChickenSandwich + * 15 Fries + 4 LowfatMilk + 120 OrangeJuice >= 100 * Calc: 30 Cheeseburger + 20 HamSandwich + 25 Hamburger + 15 FishSandwich + * 15 ChickenSandwich + 20 SausageBiscuit + 30 LowfatMilk + 2 OrangeJuice >= 100 * Iron: 20 Cheeseburger + 20 HamSandwich + 20 Hamburger + 10 FishSandwich + * 8 ChickenSandwich + 2 Fries + 15 SausageBiscuit + 2 OrangeJuice >= 100 * Volume: 4 Cheeseburger + 7.500000000 HamSandwich + 3.500000000 Hamburger + 5 FishSandwich + * 7.300000000 ChickenSandwich + 2.600000000 Fries + 4.100000000 SausageBiscuit + 8 LowfatMilk + 12 OrangeJuice <= 75 * Bounds * End */ """ from mindoptpy import * if __name__ == "__main__": MDO_INFINITY = MdoModel.get_infinity() req = \ { # requirement: ( lower bound, upper bound) "Cal" : ( 2000, MDO_INFINITY), "Carbo" : ( 350, 375), "Protein" : ( 55, MDO_INFINITY), "VitA" : ( 100, MDO_INFINITY), "VitC" : ( 100, MDO_INFINITY), "Calc" : ( 100, MDO_INFINITY), "Iron" : ( 100, MDO_INFINITY), "Volume" : (-MDO_INFINITY, 75) } food = \ { # food : ( lower bound, upper bound, cost) "Cheeseburger" : ( 0, MDO_INFINITY, 1.84), "HamSandwich" : ( 0, MDO_INFINITY, 2.19), "Hamburger" : ( 0, MDO_INFINITY, 1.84), "FishSandwich" : ( 0, MDO_INFINITY, 1.44), "ChickenSandwich" : ( 0, MDO_INFINITY, 2.29), "Fries" : ( 0, MDO_INFINITY, 0.77), "SausageBiscuit" : ( 0, MDO_INFINITY, 1.29), "LowfatMilk" : ( 0, MDO_INFINITY, 0.60), "OrangeJuice" : ( 0, MDO_INFINITY, 0.72) } req_value = \ { # (requirement, food ) : value ( "Cal", "Cheeseburger" ) : 510, ( "Cal", "HamSandwich" ) : 370, ( "Cal", "Hamburger" ) : 500, ( "Cal", "FishSandwich" ) : 370, ( "Cal", "ChickenSandwich" ) : 400, ( "Cal", "Fries" ) : 220, ( "Cal", "SausageBiscuit" ) : 345, ( "Cal", "LowfatMilk" ) : 110, ( "Cal", "OrangeJuice" ) : 80, ( "Carbo", "Cheeseburger" ) : 34, ( "Carbo", "HamSandwich" ) : 35, ( "Carbo", "Hamburger" ) : 42, ( "Carbo", "FishSandwich" ) : 38, ( "Carbo", "ChickenSandwich" ) : 42, ( "Carbo", "Fries" ) : 26, ( "Carbo", "SausageBiscuit" ) : 27, ( "Carbo", "LowfatMilk" ) : 12, ( "Carbo", "OrangeJuice" ) : 20, ( "Protein", "Cheeseburger" ) : 28, ( "Protein", "HamSandwich" ) : 24, ( "Protein", "Hamburger" ) : 25, ( "Protein", "FishSandwich" ) : 14, ( "Protein", "ChickenSandwich" ) : 31, ( "Protein", "Fries" ) : 3, ( "Protein", "SausageBiscuit" ) : 15, ( "Protein", "LowfatMilk" ) : 9, ( "Protein", "OrangeJuice" ) : 1, ( "VitA", "Cheeseburger" ) : 15, ( "VitA", "HamSandwich" ) : 15, ( "VitA", "Hamburger" ) : 6, ( "VitA", "FishSandwich" ) : 2, ( "VitA", "ChickenSandwich" ) : 8, ( "VitA", "Fries" ) : 0, ( "VitA", "SausageBiscuit" ) : 4, ( "VitA", "LowfatMilk" ) : 10, ( "VitA", "OrangeJuice" ) : 2, ( "VitC", "Cheeseburger" ) : 6, ( "VitC", "HamSandwich" ) : 10, ( "VitC", "Hamburger" ) : 2, ( "VitC", "FishSandwich" ) : 0, ( "VitC", "ChickenSandwich" ) : 15, ( "VitC", "Fries" ) : 15, ( "VitC", "SausageBiscuit" ) : 0, ( "VitC", "OrangeJuice" ) : 4, ( "VitC", "LowfatMilk" ) : 120, ( "Calc", "Cheeseburger" ) : 30, ( "Calc", "HamSandwich" ) : 20, ( "Calc", "Hamburger" ) : 25, ( "Calc", "FishSandwich" ) : 15, ( "Calc", "ChickenSandwich" ) : 15, ( "Calc", "Fries" ) : 0, ( "Calc", "SausageBiscuit" ) : 20, ( "Calc", "LowfatMilk" ) : 30, ( "Calc", "OrangeJuice" ) : 2, ( "Iron", "Cheeseburger" ) : 20, ( "Iron", "HamSandwich" ) : 20, ( "Iron", "Hamburger" ) : 20, ( "Iron", "FishSandwich" ) : 10, ( "Iron", "ChickenSandwich" ) : 8, ( "Iron", "Fries" ) : 2, ( "Iron", "SausageBiscuit" ) : 15, ( "Iron", "LowfatMilk" ) : 0, ( "Iron", "OrangeJuice" ) : 2, ( "Volume", "Cheeseburger" ) : 4, ( "Volume", "HamSandwich" ) : 7.5, ( "Volume", "Hamburger" ) : 3.5, ( "Volume", "FishSandwich" ) : 5, ( "Volume", "ChickenSandwich" ) : 7.3, ( "Volume", "Fries" ) : 2.6, ( "Volume", "SausageBiscuit" ) : 4.1, ( "Volume", "LowfatMilk" ) : 8, ( "Volume", "OrangeJuice" ) : 12 } # ----Step 1. Create a model and change the parameters. model = MdoModel() try: # ----Step 2. Input model. # Change to minimization problem. model.set_int_attr("MinSense", 1) # Add variables. var = {} for food_name, food_data in food.items(): var[food_name] = model.add_var(food_data[0], food_data[1], food_data[2], None, food_name, False) # Add constraints. cons = {} for req_name, req_data in req.items(): expr = MdoExprLinear() for food_name in food.keys(): expr += req_value[req_name, food_name] * var[food_name] cons[req_name] = model.add_cons(req_data[0], req_data[1], expr, req_name) # ----Step 3. Solve the problem and populate the result. model.solve_prob() model.display_results() time.sleep(1) #for print status_code, status_msg = model.get_status() if status_msg == "OPTIMAL": print("----\n") print("The solver terminated with an OPTIMAL status (code {0}).".format(status_code)) print("目标函数总收益是: {0}".format(model.get_real_attr("PrimalObjVal"))) print("原始解是:") for var_name,var_val in var.items(): primal_soln = var_val.get_real_attr("PrimalSoln") print("{0:>20} : {1}".format(var_name,primal_soln)) else: print("Optimizer terminated with a(n) {0} status (code {1}).".format(status_msg, status_code)) except MdoError as e: print("Received Mindopt exception.") print(" - Code : {}".format(e.code)) print(" - Reason : {}".format(e.message)) except Exception as e: print("Received exception.") print(" - Reason : {}".format(e)) finally: # Step 4. Free the model. model.free_mdl()
点击运行后,得到的结果如下:
Start license validation (current time : 17-JAN-2023 23:54:14). License validation terminated. Time : 0.003s Model summary. - Num. variables : 9 - Num. constraints : 8 - Num. nonzeros : 67 - Bound range : [5.5e+01,2.0e+03] - Objective range : [6.0e-01,2.3e+00] - Matrix range : [1.0e+00,5.1e+02] Presolver started. Presolver terminated. Time : 0.000s Simplex method started. Model fingerprint: =IGYvFmb5d2dgF2dud3b Iteration Objective Dual Inf. Primal Inf. Time 0 0.00000e+00 0.0000e+00 4.0937e+01 0.00s 3 1.48557e+01 0.0000e+00 0.0000e+00 0.00s Postsolver started. Simplex method terminated. Time : 0.001s Optimizer summary. - Optimizer used : Simplex method - Optimizer status : OPTIMAL - Total time : 0.002s Solution summary. Primal solution - Objective : 1.4855737705e+01 ---- The solver terminated with an OPTIMAL status (code 1). 目标函数总收益是: 14.855737704918033 原始解是: Cheeseburger : 4.385245901639344 HamSandwich : 0.0 Hamburger : 0.0 FishSandwich : 0.0 ChickenSandwich : 0.0 Fries : 6.147540983606558 SausageBiscuit : 0.0 LowfatMilk : 3.422131147540985 OrangeJuice : 0.0
方法2:命令行直接运行.py文件
上面是直接在cell中运行所有的脚本,我们也可以建立个新文档,将Python代码存在src/python_src文件夹的LP_2_diet.py文件。然后在Launcher中打开Terminal,执行python xx.py文件来运行。
您也可以下载本.py文件,在自己的电脑上安装MindOpt求解器,然后在自己电脑的环境运行。
Luancher可以点击左上角的+打方块打开,Terminal在最下方,如截图示意。打开的窗口可以拖动调整位置。
然后在Terminal命令行里运行如下指令:
cd src/python_src python LP_2_diet.py
运行得到的结果同方法1:
方法3:复制案例广场的例子
这个例子已经在MindOpt的案例广场,可以直接复制:营养调配
直接点击复制项目,会将配置的文件全部复制过来,可以直接运行。(如上图所示)
求解结果
运行结果里面打印有:
目标函数总收益是: 14.855737704918033 原始解是: Cheeseburger : 4.385245901639344 HamSandwich : 0.0 Hamburger : 0.0 FishSandwich : 0.0 ChickenSandwich : 0.0 Fries : 6.147540983606558 SausageBiscuit : 0.0 LowfatMilk : 3.422131147540985 OrangeJuice : 0.0
代表求解结果的目标函数(每日饮食餐费)最低解为:$14.86元,包含了4.39份起司汉堡,6.15份薯条,以及3.42份低脂牛乳。
联系我们
钉钉答疑群:32451444
钉钉活动群:18890022111(精彩好礼等你来拿!)
邮箱地址:solver.damo@list.alibaba-inc.com