问题描述
假设我们每人每天需要摄入一定的维生素,每种食物维生素的含量不一样,而且每种食物摄入有一定的限制,每种食物的价格均不相同,我们如何决策,能够满足每天的维生素需求,并且最小化购买食物所花费的金额。这个优化问题也可以使用数学规划的方法来建模和求解。
业务调研、数据量化、数学建模
在使用优化技术的时候,需要更详细的调研业务的需求,整理相关的业务逻辑和数据,并量化表示它。然后采用数学规划的方法进行数学建模。
此部分细节较多,可在案例营养搭配中查阅细节,此处我们仅列出数学公式如下,参数部分请点击案例链接查看:
其中j代表每种食物,i代表食物中不同的营养种类,c代表获取食物的代价,x代表每天对食物的摄入量,n为对营养需求的上下限。
对应以上的约束有:
摄入的营养介于营养需求的上下限之间
源代码
MindOpt支持多种编程语言或者建模语言来调用。此处仅列出一种供参考:
MindOpt APL建模语言调用
MindOpt APL建模语言的源代码(可在营养搭配上试运行):
##====MindOpt APL 建模语言版本代码====
# diet.mapl
set NUTR := { "A", "B1", "B2", "C" };
set FOOD := {"BEEF", "CHK", "FISH", "HAM", "MCH", "MTL", "SPG", "TUR"} ;
set F:= {"cost", "f_min", "f_max"};
set N:= {"n_min", "n_max"};
param data1[FOOD * F] :=
| "cost" , "f_min" , "f_max" |
|"BEEF" | 3.19 , 0 , 100 |
|"CHK" | 2.59 , 0 , 100 |
|"FISH" | 2.29 , 0 , 100 |
|"HAM" | 2.89 , 0 , 100 |
|"MCH" | 1.89 , 0 , 100 |
|"MTL" | 1.99 , 0 , 100 |
|"SPG" | 1.99 , 0 , 100 |
|"TUR" | 2.49 , 0 , 100 |;
param data2[NUTR * N] :=
| "n_min", "n_max"|
|"A" | 700, 10000 |
|"C" | 700, 10000 |
|"B1" | 700, 10000 |
|"B2" | 700, 10000 |;
param amt[FOOD * NUTR] :=
| "A", "C", "B1", "B2"|
|"BEEF" | 60, 20, 10, 15 |
|"CHK" | 8, 0, 20, 20 |
|"FISH" | 8, 10, 15, 10 |
|"HAM" | 0, 40, 35, 10 |
|"MCH" | 15, 35, 0, 15 |
|"MTL" | 70, 30, 15, 0 |
|"SPG" | 0, 50, 25, 15 |
|"TUR" | 60, 0, 15, 0 |;
var x[j in FOOD] >= data1[j, "f_min"] <= data1[j, "f_max"];
minimize Total_Cost: sum {j in FOOD} : data1[j, "cost"] * x[j];
subto Diet: forall {i in NUTR}
data2[i, "n_min"] <= sum {j in FOOD}: amt[j, i] * x[j] <= data2[i, "n_max"];
#------------------------------
print "-----------------用MindOpt求解---------------";
option solver mindopt; # (可选)指定求解用的求解器,默认是MindOpt
solve; # 求解
print "-----------------Display---------------";
display; # 展示结果
print "最低价格是:";
print sum {<j> in FOOD} data1[j, "cost"] * x[j];
结果和结果用法
不同代码日志打印不一样。部分结果日志打印如下所示:
...
Model summary.
- Num. variables : 8
- Num. constraints : 4
- Num. nonzeros : 25
- Bound range : [1.0e+02,1.0e+04]
- Objective range : [1.9e+00,3.2e+00]
- Matrix range : [8.0e+00,7.0e+01]
...
Simplex method terminated. Time : 0.007s
OPTIMAL; objective 101.01
...
-----------------结果---------------
最低价格是:
101.0110471806674
目标的最优解是:101。更多解的细节,如决策变量的取值、验证约束是否正确,可以通过print命令打印来显示,变量的取值也可以从程序运行写的.sol文件里面获取,还可以调用display
获取所有变量的。
如运行如下指令,验证“摄入的营养是没有超过营养需求的上限”这个约束:
forall {i in NUTR }
print '维生素{}摄入量为{},摄入上限UB为{}'%i,sum {j in FOOD}: amt[j, i] * x[j],data2[i, 'n_max'] ,
sum {j in FOOD}: amt[j, i] * x[j]>= data2[i, 'n_max'];
结果如下:
维生素A摄入量为700,摄入上限UB为10000
维生素B1摄入量为699.9999999999999,摄入上限UB为10000
维生素B2摄入量为700,摄入上限UB为10000
维生素C摄入量为700,摄入上限UB为10000
运行如下代码,将输出打印为csv格式,作为营养搭配问题的解决方案:
print "{}, {},{}" % "食物","购买食物的成本", "食物摄入量" : "Results.csv";
close "Results.csv";
forall {<j> in FOOD}
print "{}, {},{}" % j,data1[j, "cost"] * x[j], x[j] >> "Results.csv";
close "Results.csv";
运行结果如下:
即,建议吃的食物是鸡肉(CHK), 芝士通心粉(MCH), 肉饼(MTL), 意大利面 (SPG),满足约束需求后购买食物的最低价格为101。
文档内容是否对您有帮助?