解答被划分成 4 部分:
对于这个特定的问题来说,我们可以看到解决方案是 OPTIMAL 的,Giapetto 每周最大收益是 180 美元。
Finishing 约束的状态是 NU(St 列)。NU 表示这个约束的上界有一个非基本变量。如果我们了解一些基本的运筹学理论,就可以构建 simplex 场景并将之提取出来。如果不了解运筹学理论,下面是一个实际的解释。
不论何时约束达到自己的上界或下界时,我们就称之为是一个边界约束(bounded constraint)。边界约束阻碍了目标函数达到更为优化的值。例如我们可以认为它是一个音量调节旋钮,现在它已经无法再进行调节了。当这种情况发生时,glpsol 就会将约束状态显示为 NU 或 NL(分别对应上界和下界的情况),它还会显示边界(marginal)值,也称为假定价格(shadow price)。边界值是约束如果可以放松一个单位(如果音量调节旋钮可以再调节一点点)目标函数可以改进的值。注意这种改进依赖于我们的目标是要对目标函数进行最大化还是最小化。举例来说, Giapetto 问题所寻求的就是最大化,边界值为 1 就表示如果我们可以增加一个小时的修整工时,目标函数就可以增大 1(我们知道这是要多 一个小时,而不是少一个小时,这是因为修整工时约束是一个上界)。
木工和士兵的需求约束是类似的。对于木工约束来说,注意它也有一个上界。因此,这个约束中放松一个单位(增加一个小时)可以使目标函数的优化值增加边界值 1,成为 181。
然而,士兵需求是没有边界的,因此其状态是 B,这个约束中的放松不会改变目标函数的优化值。
一次只尝试放松每个边界约束一个单位,解决修改后的问题,看一下目标函数的优化值发生了什么变化。还要检查修改无边界约束的值不会对解答造成任何变化,这正是我们期望的。
最后,glpsol 的报告给出了决策变量的值:x1 = 20 和 x2 = 60。这就告诉 Giapetto 它应该生产 20 个士兵和 60 辆火车才能实现每周收益的最大化。
Giapetto 的问题很小。我们可能会纳闷,在有更多决策变量和约束的问题中,我们只能分别逐一声明每个变量和每个约束吗?如果希望调节问题中的数据(例如士兵的销售价格)应该如何做呢?我们只能逐一修改这些值出现的地方吗?下一节将讨论这个问题。
MathProg 模型通常都有一个模型段和一个数据段,有时可以在两个不同的文件中。这样 glpsol 就可以简单地使用不同的数据集来解析某个模型,从而找到对这些新数据应该采用哪种解决方案。下面的列表以更优雅的方式说明了 Giapetto 的问题:
清单 4. 使用一个模型段和一个数据段来分析 Giapetto 问题:giapetto2.mod
1 #
2 # Giapetto's problem
3 #
4 # This finds the optimal solution for maximizing Giapetto's profit
5 #
6
7 /* Set of toys */
8 set TOY;
9
10 /* Parameters */
11 param Finishing_hours {i in TOY};
12 param Carpentry_hours {i in TOY};
13 param Demand_toys {i in TOY};
14 param Profit_toys {i in TOY};
15
16 /* Decision variables */
17 var x {i in TOY} >=0;
18
19 /* Objective function */
20 maximize z: sum{i in TOY} Profit_toys[i]*x[i];
21
22 /* Constraints */
23 s.t. Fin_hours : sum{i in TOY} Finishing_hours[i]*x[i] <= 100;
24 s.t. Carp_hours : sum{i in TOY} Carpentry_hours[i]*x[i] <= 80;
25 s.t. Dem {i in TOY} : x[i] <= Demand_toys[i];
26
27
28 data;
29 /* data section */
30
31 set TOY := soldier train;
32
33 param Finishing_hours:=
34 soldier 2
35 train 1;
36
37 param Carpentry_hours:=
38 soldier 1
39 train 1;
40
41 param Demand_toys:=
42 soldier 40
43 train 6.02E+23;
44
45 param Profit_toys:=
46 soldier 3
47 train 2;
48
49 end;
|
我们并没有使用两个单独的文件,而是在一个具有模型段(第 1 行 到第 27 行)和一个数据段(第 28 行到第 49 行)的文件中声明了这个问题。
第 8 行定义了一个 SET。SET 是一个元素的值域。例如,如果我声明数学变量 xi, for all i in {1;2;3;4},就说明 x 是一个包含 4 个元素的数组,因此我们就可以使用 x1、x2、x3、x4 了。在这个例子中,{1;2;3;4} 就是这个集合。因此,在 Giapetto 问题中,有一个集合 TOY。这个集合的实际值是什么呢?在这个文件的数据段中。请查看第 31 行。它定义了 TOY 集合包含 soldier 和 train。哦,这个集合不是一个数字类型的范围。那么这如何实现呢?GLPK 是以一种非常有趣的方法来处理这个问题的。稍后我们就会看到怎么样使用这种方法。现在,请习惯数据段中 SET 声明所使用的语法:
set label := value1 value2 ... valueN ;
第 11、12 和 13 行定义了这个问题的参数。一共有三个参数: