收藏 分享(赏)

GNU 线性编程工具包.doc

上传人:ysd1539 文档编号:4874623 上传时间:2019-01-18 格式:DOC 页数:49 大小:661KB
下载 相关 举报
GNU 线性编程工具包.doc_第1页
第1页 / 共49页
GNU 线性编程工具包.doc_第2页
第2页 / 共49页
GNU 线性编程工具包.doc_第3页
第3页 / 共49页
GNU 线性编程工具包.doc_第4页
第4页 / 共49页
GNU 线性编程工具包.doc_第5页
第5页 / 共49页
点击查看更多>>
资源描述

1、GNU 线性编程工具包(GNU Linear Programming Kit),第 1 部分: 线性优化简介为复杂数学问题寻找最佳解决方案级别: 中级Rodrigo Ceron (), 资深软件工程师, IBM2006 年 9 月 04 日GNU Linear Programming Kit 对于解决具有多种约束的数学问题来说是一个功能非常强大的工具。本文简要介绍了如何使用 GLPK(glpsol 客户机工具)和 GNU MathProg 语言来解决 Giapetto 的 Woodcarving 公司(一家玩具制造商)的作业优化问题。简介 “线性编程是一个用来解决优化问题的工具。在 1947

2、年,George Dantzig 开发了一种效率方法 simplex 算法 来解决线性编程的问题。由于 simplex 算法的出现,线性编程已经在工业界、银行界、教育界、林业、石油行业以及运输业界中广泛地用来解决优化问题。在对财富 500 强公司的调查中,85% 的被调查者都说他们已经使用了线性编程。” 引自 Operations Research: Applications and Algorithms, 4th Edition,Wayne L. Winston 著(Thomson,2004);请参看下面 参考资料 中的链接。 有很多工具都可以用来解决线性编程的问题。尽管有些专用工具都非常出

3、名,但是开放源码社区中的很多人可能并不了解免费的 GLPK 工具。 本系列文章一共 3 篇,将逐渐介绍 GLPK 的功能和用法;本文是其中的第 1 篇,在本文中,我们将对 GLPK 简要进行介绍,然后展示并应用 GLPK 中的 文档选项未显示需要 JavaScript 的文档选项GNU MathProg Language。 如果我们仅仅从运筹学理论入手,希望学习进行建模和解决线性编程的问题,那么本文就是一个很好的指南。 GNU Linear Programming KitGNU Linear Programming Kit(GLPK)是一个使用了众所周知的运筹学算法来解决线性问题的程序库。这些

4、程序实现了simplex 算法、branch and bound 算法、primal-dual interior point 算法以及很多其他算法。请查看 GLPK 下载包中所包含的 GLPK 手册以便了解更多的内容。(要下载 GLPK,请参看 参考资料 一节中给出的 gnu.org 上面 GLPK 页面的链接。) GLPK 不是一个程序 它无法运行,也没有 main()函数。实际上,客户机需要将问题数据通过 GLPK API 提供给算法程序,并接收结果。GLPK 有一个默认的客户机,即 glpsol 程序,它可以与这个 API 进行交互。通常诸如 glpsol 之类的程序都被称为 solve

5、r,而不是客户机,因此从现在开始我们就会看到这个术语。 GNU MathProg 建模语言GNU MathProg 建模语言非常简单,但却可以很好地声明线性问题。通常,问题的声明包括: 问题决策变量。 目标函数。注意 目标( objective) 是一个名词,而不是一个形容词。这个名字是运筹学理论中的标准术语。 问题约束。 问题参数(数据)。让我们从一个简单的两变量的例子入手:Giapetto 的 Woodcarving 公司。 Giapetto 的 Woodcarving 公司这个问题引自于 Operations Research: Giapetto 的 Woodcarving 公司生产两种

6、木头制作的玩具:士兵和火车。一个士兵的销售价格为 27 回页首回页首美元,需要耗费价值 10 美元的原料。制造每个士兵需要耗费 Giapetto 的可变人力成本和间接成本一共 14 美元。一辆火车的销售价格为 21 美元,需要耗费价值 9 美元的原料。制造每辆火车需要耗费 Giapetto 的可变人力成本和间接成本一共 10 美元。这家木头士兵和火车的制造商需要两类熟练工人:木工和修整工。一个士兵需要 2 小时的修整工劳动和 1 小时的木工劳动。一辆火车需要 1 小时的修整工劳动和 1 小时的木工劳动。每周 Giapetto 可以获得所有必需的原料,但是只能提供 100 个修整工时和 80 个

7、木工工时。市场对于火车的需求是无限的,但是每周最多可以销售 40 个士兵。 Giapetto 希望能够使每周的收益(收入 - 成本)最大化。下面我们对这个问题的重要信息和假设小结一下: 1. 有两种木制玩具:士兵和火车。 2. 一个士兵的销售价格为 27 美元,需要耗费价值 10 美元的原料,另外需要耗费可变人力成本和间接成本一共 14 美元。 3. 一辆火车的销售价格为 21 美元,需要耗费价值 9 美元的原料,另外需要耗费可变人力成本和间接成本一共 10 美元。 4. 一个士兵需要 2 小时的修整工劳动和 1 小时的木工劳动。 5. 一辆火车需要 1 小时的修整工劳动和 1 小时的木工劳动

8、。 6. 每周最多可以获得 100 个修整工时和 80 个木工工时。 7. 每周对于火车的需求是无限的,但是最多可以销售 40 个士兵。 我们的目标是确定每周生产士兵和火车的数量,从而可以使每周的收益最大化。 建模要对一个线性问题进行建模,必须首先确定决策变量,这是因为这些变量在每个 simplex 算法循环中都会变化,并决定了目标函数的值,这样才会产生优化解决方案。在 Giapetto 的商店中,目标函数是收益,这是一个每周生产的士兵和火车的函数。因此,这个问题中的两个决策变量是: x1:每周生产的士兵数量 x2:每周生产的火车数量 确定了决策变量之后,这个问题的目标函数就简化为每个玩具的收

9、入减去成本,这是 x 1和 x 2的一个函数。 回页首请注意,收益线性依赖于 x 1和 x 2 这是一个线性问题。 乍一看,收益可以通过简单地增大 x 1和 x2来实现最大化。然而,如果生活是如此简单,那就让我们都开始生产火车和士兵,并迁居到 Caribbean 好了!不幸的是,有很多约束会对可以选择的决策变量造成影响(否则这个模型很可能就是错的)。 回想一下我们对这个问题的 假设。前三条确定了决策变量和目标函数。第 4 个假设和第 6 个假设说完成士兵的制造需要耗费一定的木工和修整工工时。此处的约束是 Giapetto 并没有无限的木工和修整工工时。这就是约束!下面我们来分析并澄清这个问题。

10、 一个士兵需要 2 小时的修整工时劳动,Giapetto 每周最多有 100 小时的修整工劳动力,因此每周就不能生产超过 50 个士兵。类似地,木工工时的约束也使它不能每周生产超过 80 个士兵。注意第一个约束比第二个约束更为严格。第一个约束实际上是第二个约束的一个子集,因此第二个约束实际上是冗余的。 上面这段描述展示了如何对优化问题进行建模,但这只是不完全的一个分析,因为所有必需的变量都还没有充分考虑。这尚不是 Giapetto 问题的彻底解决方案。那么我们应该如何解决这个问题呢? 我们首先通过分析约束因素来发现所有的约束条件。首先,到底是什么约束的修整工时?由于士兵和火车都需要修整时间,因

11、此它们都需要考虑在 内。假设要制造 10 个士兵和 20 辆火车。这所需要的修整工时是 10 乘以 2 小时(用来生产士兵)加上 20 乘以 1 小时(用来生产火车),总共是 40 小时的修整劳动力。按照决策变量的术语来说,通用的约束为: 有很多 (x 1,x2) 对能够满足这个不等式,因此它无法确定 Giapetto 商店的最佳组合。 现在我们已经知道了修整工时的约束,同样也可以得出木工工时的约束: 非常不错!这个问题只剩下一个约束了。记得每周对士兵的需求么?根据问题描述,每周最多可以生产 40 个士兵: 对于火车的需求是无限的,因此对它就没有什么约束了。这个模型就完成了,它包括以下等式:

12、注意最后一个约束。它确保决策变量的值都是正数。问题并没有显式地说明这一点,但它却非常重要(也很显然)。 现在 GLPK 可以解析这个模型了(由于 GLPK 很擅长解决线性优化问题)。 一点理论知识下面我们来了解以下问题的解析空间。有了这两个决策变量,它就有了两个维度。图 1. Giapetto 的极大域回页首(x1,x2) 超出第一象限(其中所有值都为正数)的结果都已经被舍弃了。然而,我们需要注意这个解析空间仍然是无限的(这依然 可能 成为我希望移居 Caribbean 的一种情况!) 正如我们给出的约束一样,这个无限的解析空间达到了边界。有了上面的 不等式 6,结果就更加有趣了。图 2. G

13、iapetto 考虑修整约束时的解析域这个解析空间包含了 (x 1,x2) 在第一象限的所有可能值,它们可以满足修整工时约束。 在加上 不等式 7 之后,结果就收缩了。图 3. Giapetto 考虑修整约束和木工约束时的解析域注意,这个解析空间更小。这意味着其中只有更少的 (x 1,x2) 值。在应用不等式 8 之后,结果还会进一步变小。图 4. Giapetto 的可行域这样解析空间更小了。这个可以满足所有约束的解析空间称为 可行域( feasible region) 。图 4 给出了 Giapetto 商店的可行域。这个区域中任何 (x1,x2) 对都是这个问题的一个可能解决方案。现在的

14、问题是:哪个值能够使得 Giapetto 的收益最大化呢? 使用 GLPK 来解析模型GLPK 对于解析这个问题来说是一个很好的工具。Giapetto 问题中的数学公式需要使用 GNU MathProg 语言进行编写。需要声明的关键内容有: 决策变量 目标函数 约束 问题数据集 下面的代码显示了如何使用 MathProg 来解决 Giapetto 回页首问题。代码中的行号都不是代码本身的一部分。添加这些行号只是为了能够方便地对代码进行引用。 清单 1. Giapetto 问题的第一个解决方案:giapetto.sol1 #2 # Giapettos problem3 #4 # This fin

15、ds the optimal solution for maximizing Giapettos profit5 #67 /* Decision variables */8 var x1 =0; /* soldier */9 var x2 =0; /* train */1011 /* Objective function */12 maximize z: 3*x1 + 2*x2;1314 /* Constraints */15 s.t. Finishing : 2*x1 + x2 = 0约束,正如我们在第 8 行和第 9 行中看到的一样。GNU MathProg 中的每条语句都必须以分号(;)

16、结束。记住 x 1表示 soldier的数量,x 2表示 train的数量。这些变量应该分别称为 soldiers和 trains,但是这可能会把读者中的数学界人士搞得混乱不堪。 第 12 行声明了目标函数。线性问题可以是最大化问题,也可以是最小化问题。记住,Giapetto 的数学模型是一个最大化问题,因此我们应该使用 maximize作为关键字,而不是相反的 minimize 关键字。目标函数称为 z,它等于 3x 1+ 2x2。请注意: 冒号(:)字符分隔开了目标函数及其定义。 星号(*)字符表示乘法,类似地,加号(+ )、减号(-)和斜线(/)字符分别表示加法、减法和除法。 第 15、

17、16、17 行定义了约束。尽管 s.t.对于声明一个约束来说并不需要在行首,但是这样可以提高代码的可读性。 这三个 Giapetto 约束分别被标记成 Finishing、 Carpentry 和 Demand。每个都是作为一个数学模型来声明的。符号 =表示不等式。不要忘记每个声明末尾的 ;。 每个 GNU MathProg 文件必须要以 end;结束,正如我们在 19 行看到的一样。 现在,glpsol 可以使用这个文件作为输入。但是请等一下,这个问题的数据部分在什么地方呢?噢,这个问题是如此简单,问题数据都作为声明中决策变量的系数直接包含在了目标函数和约束声明中。举例来说,在目标函数中,系

18、数 3和 1就是问题数据集的一部分。当我使用数据集重写这个问题时,大家就可以非常清楚这到底是如何工作的。现在,我们不用关心这个问题。 使用 .mod 作为 MathProg 输入文件的扩展名并将答案重定向到一个扩展名为 .sol的文件中是一个好习惯。不过这并不是必需的要求,您可以使用任何自己喜欢的文件名和扩展名。这个例子中 Giapetto 的 MathProg 文件是 giapetto.mod,输出结果在 giapetto.sol中。现在,请在自己喜欢的终端中运行 glpsol: glpsol -m giapetto.mod -o giapetto.sol这个命令使用了两个 glpsol 选

19、项: -m选项告诉 glpsol 输入是使用 GNU MathProg 编写的。 -o选项告诉 glpsol 将自己的输出结果发送到 giapetto.sol中。 解答报告现在就在 giapetto.sol中了,但是有关 GLPK 所消耗的时间和内存信息会显示在标准输出上: 清单 2. glpsol 的输出 ceroncurly $ glpsol -m giapetto.mod -o giapetto.solReading model section from giapetto.real.mod.19 lines were readGenerating z.Generating Finishi

20、ng.Generating Carpentry.Generating Demand.Model has been successfully generatedlpx_simplex: original LP has 4 rows, 2 columns, 7 non-zeroslpx_simplex: presolved LP has 2 rows, 2 columns, 4 non-zeroslpx_adv_basis: size of triangular part = 2* 0: objval = 0.000000000e+00 infeas = 0.000000000e+00 (0)*

21、2: objval = 1.400000000e+02 infeas = 0.000000000e+00 (0)OPTIMAL SOLUTION FOUNDTime used: 0.0 secsMemory used: 0.1M (151326 bytes)lpx_print_sol: writing LP problem solution to giapetto.sol.所生成的报告显示 glpsol 会读取这个模型,调用一个 GLPK API 函数来生成目标函数,然后调用另外一个 API 函数来生成约束。在生成模型之后,glpsol 简要地解释了 GLPK 在内部是如何对这个问题进行处理的

22、。最后是有关解答和 GLPK 为解答这个问题所消耗的资源的信息,这个解答被明确说明是优化的。 这非常棒,但是决策变量的实际优化值是什么呢?它们都保存在 giapetto.sol文件中: 清单 3. Giapetto 问题的解答:giapetto.solProblem: giapettoRows: 4Columns: 2Non-zeros: 7Status: OPTIMALObjective: z = 180 (MAXimum)No. Row name St Activity Lower bound Upper bound Marginal- - - - - - -1 z B 1802 Fini

23、shing NU 100 100 13 Carpentry NU 80 80 14 Demand B 20 40No. Column name St Activity Lower bound Upper bound Marginal- - - - - - -1 x1 B 20 02 x2 B 60 0Karush-Kuhn-Tucker optimality conditions:KKT.PE: max.abs.err. = 0.00e+00 on row 0max.rel.err. = 0.00e+00 on row 0High qualityKKT.PB: max.abs.err. = 0

24、.00e+00 on row 0max.rel.err. = 0.00e+00 on row 0High qualityKKT.DE: max.abs.err. = 0.00e+00 on column 0max.rel.err. = 0.00e+00 on column 0High qualityKKT.DB: max.abs.err. = 0.00e+00 on row 0max.rel.err. = 0.00e+00 on row 0High qualityEnd of output解答被划分成 4 部分: 有关问题和目标函数优化值的信息 有关目标函数状态和约束的确切信息 有关决策变量的

25、优化值的确切信息 有关优化条件的信息(如果有的话) 对于这个特定的问题来说,我们可以看到解决方案是 OPTIMAL的,Giapetto 每周最大收益是 180 美元。 Finishing 约束的状态是 NU( St 列)。NU表示这个约束的上界有一个非基本变量。如果我们了解一些基本的运筹学理论,就可以构建 simplex 场景并将之提取出来。如果不了解运筹学理论,下面是一个实际的解释。 不论何时约束达到自己的上界或下界时,我们就称之为是一个 边界约束( bounded constraint) 。边界约束阻碍了目标函数达到更为优化的值。例如我们可以认为它是一个音量调节旋钮,现在它已经无法再进行调

26、节了。当这种情况发生时,glpsol 就会将约束状态显示为 NU或 NL(分别对应上界和下界的情况),它还会显示 边界( marginal) 值,也称为 假定价格( shadow price) 。 边界值是约束如果可以放松一个单位(如果音量调节旋钮可以再调节一点点)目标函数可以改进的值。注意这种改进依赖于我们的目标是要对目标函数进行最大化还 是最小化。举例来说, Giapetto 问题所寻求的就是最大化,边界值为 1 就表示如果我们可以增加一个小时的修整工时,目标函数就可以 增大 1(我们知道这是要 多 一个小时,而不是少一个小时,这是因为修整工时约束是一个上界)。 木工和士兵的需求约束是类似

27、的。对于木工约束来说,注意它也有一个上界。因此,这个约束中放松一个单位(增加一个小时)可以使目标函数的优化值增加边界值 1,成为 181。 然而,士兵需求是没有边界的,因此其状态是 B,这个约束中的放松不会改变目标函数的优化值。 一次只尝试放松每个边界约束一个单位,解决修改后的问题,看一下目标函数的优化值发生了什么变化。还要检查修改无边界约束的值不会对解答造成任何变化,这正是我们期望的。 最后,glpsol 的报告给出了决策变量的值:x 1= 20和 x 2= 60。这就告诉 Giapetto 它应该生产 20 个士兵和 60 辆火车才能实现每周收益的最大化。 Giapetto 的问题很小。我

28、们可能会纳闷,在有更多决策变量和约束的问题中,我们只能分别逐一声明每个变量和每个约束吗?如果希望调节问题中的数据(例如士兵的销售价格)应该怎样做呢?我们只能逐一修改这些值出现的地方吗?下一节将讨论这个问题。 在 Giapetto 问题中使用模型和数据段MathProg 模型通常都有一个模型段和一个数据段,有时可以在两个不同的文件中。这样 glpsol 就可以简单地使用不同的数据集来解析某个模型,从而找到对这些新数据应该采用哪种解决方案。下面的列表以更优雅的方式说明了 Giapetto 的问题: 清单 4. 使用一个模型段和一个数据段来分析 Giapetto 问题:giapetto2.mod1

29、#2 # Giapettos problem3 #4 # This finds the optimal solution for maximizing Giapettos profit5 #67 /* Set of toys */8 set TOY;910 /* 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;1516 /* Decision variables

30、*/17 var x i in TOY =0;1819 /* Objective function */20 maximize z: sumi in TOY Profit_toysi*xi;2122 /* Constraints */23 s.t. Fin_hours : sumi in TOY Finishing_hoursi*xi = 0;18回页首19 /* objective function */20 minimize z: sumi in FOOD Costi*xi;2122 /* Constraints */23 s.t. constj in NEED : sumi in FOO

31、D NutrTablei,j*xi = Needj;2425 /* data section */26 data;2728 set FOOD := Brownie “Ice cream“ soda cake;29 set NEED := Calorie Chocolate Sugar Fat;3031 param NutrTable: Calorie Chocolate Sugar Fat:=32 Brownie 400 3 2 233 “Ice cream“ 200 2 2 434 soda 150 0 4 135 cake 500 0 4 5;3637 param Cost:=38 Bro

32、wnie 0.539 “Ice cream“ 0.240 soda 0.341 cake 0.8;4243 param Need:=44 Calorie 50045 Chocolate 646 Sugar 1047 Fat 8;4849 end;第 8 行和第 9 行定义了两个集合:FOOD和 NEED,但是这两个集合的元素都是在第 28 行和 29 行的数据部分声明的。FOOD集合中包含了前面给出的 4 种食物约束。 由于空格字符被用来分隔集合中的元素,因此 Ice cream元素(这里没有使用 icecream)需要在名字两边使用双引号括起来。如果我们希望在 MathProg 名字中使用非

33、 ASCII 字符,那么即使名字中间没有空格,也应该使用双引号将它们括起来。 现在我们回到模型部分上来。NEED集合中保存了 4 种饮食的需要。第 12、13 和 14 行定义了问题的参数:Need、 Cost和 NutrTable(营养表)。每个 FOOD都有一个成本值。对于 NEED集合中的每种营养都有一定的需求量。我们可以试图为每个集合使用不同的命名索引变量;这是一个好主意,尤其是在进行调试时更为突出。在这种情况中,FOOD集合使用 i,而 NEED集合使用 j。数据部分中的 Cost和 Need参数是在 37 行到 47 行进行定义的。 在 12 行上定义的营养表和在 31 到 35

34、行给出的数据有两个维度,这是因为每种食物都提供了多种营养价值。因此,营养表 NutrTable就是 FOOD 和 NEED 集合之间的一个映射。注意行和列的次序与元素在每个集合中的顺序是相同的,索引变量名在第 12、13 和 14 行是一致的。 在这个练习中,i轴是行,j 轴是列,这符合大部分数学专才的习惯。这个两维参数声明(最多 N 列 M 行)的语法如下: 清单 2. 两维参数声明的语法param label : J_SET_VAL_1 J_SET_VAL_2 . J_SET_VAL_N :=I_SET_VAL_1 param_1_1 param_1_2 . param_1_NI_SET_

35、VAL_2 param_2_1 param_2_2 . param_2_N. . . . .I_SET_VAL_M param_M_1 param_M_2 . param_M_N;不要忘记第一行末尾的 :=以及最后一行末尾的 ;符号。 第 17 行声明了决策变量,并声明每个决策变量都不能是负数。这是一个非常简单的定义,因为数组 x是使用 FOOD集合中的元素自动定义的。 第 20 行的目标函数要对食物的总体成本实现最小化,该值是每个决策变量(食物数量)乘以每单位食物成本的结果。注意 i是 FOOD集合的索引。 第 23 行声明了所有这 4 种食品的约束。这是采用非常精简的风格来编写的,因此需要

36、特别注意!冒号 :左边的定义告诉 GLPK 为 NEED集合中的每种需要创建一个约束: constCalorie、 constChocolate、 constSugar和 constFat。每个约束都要有自己的营养需求,每种食品的数量乘以每单位该食品所提供的营养需要的总和就是这种营养的总量;这个总和应该大于或等于日常饮食对该种营养的最小需求。 展开之后,这个声明应该如下所示(i会遍历整个 FOOD集合): 清单 3. 这个问题中 glpsol 的输出 Problem: dietRows: 5Columns: 4Non-zeros: 18Status: OPTIMALObjective: z =

37、 0.9 (MINimum)No. Row name St Activity Lower bound Upper bound Marginal- - - - - - -1 z B 0.92 constCalorieB 750 5003 constChocolateNL 6 6 0.0254 constSugar NL 10 10 0.0755 constFat B 13 8No. Column name St Activity Lower bound Upper bound Marginal- - - - - - -1 xBrownie NL 0 0 0.2752 xIce creamB 3

38、03 xsoda B 1 04 xcake NL 0 0 0.5Karush-Kuhn-Tucker optimality conditions:KKT.PE: max.abs.err. = 1.82e-13 on row 2max.rel.err. = 2.43e-16 on row 2High qualityKKT.PB: max.abs.err. = 0.00e+00 on row 0max.rel.err. = 0.00e+00 on row 0High qualityKKT.DE: max.abs.err. = 5.55e-17 on column 3max.rel.err. = 4

39、.27e-17 on column 3High qualityKKT.DB: max.abs.err. = 0.00e+00 on row 0max.rel.err. = 0.00e+00 on row 0High qualityEnd of output这些结果说明日常饮食最低成本(优化值)是 0.90 美元。哪些约束共同决定了这个解决方案呢? 这个报告的第二部分表明巧克力和糖的约束都是有下界的,因此这份日常饮食使用了最少的巧克力和糖。这个临界值告诉我们如果我们可以放松巧克力限制一 个单位(变成 5 盎司,而不是 6 盎司),那么目标函数就可以改进 0.025(它会从 0.9 变成 0.87

40、5)。类似地,如果我们将糖约束放松一个单位,那么目标函数就会改进 0.075。道理是很显然的:吃得越少,我们花的钱也就越少。重要的一点是要对它们进行边界和数量的常规意义的考察。例如,如果我们被告知最好吃 200 磅的巧克力,但是不能摄取任何能量,那么我们就会对此表示怀疑(如果真能如此,我们倒是会感激不尽)。 报告的第三部分则给出了决策变量的优化值:3 份冰激凌和 1 瓶可乐。巧克力松糕和菠萝芝士蛋糕都有一个临界值,因为这些值受到了符号约束的限制。如果巧克力松糕变量的临界值可以是 -1,那么目标函数就还可以改进 0.275,但是对于这个问题的具体情况来说,这当然没什么用处。 邮件问题这个问题引自

41、于“ Operations Research”:一个邮局需要有不同数目的全职员工在每周的不同时间工作(总结如下)。联盟规定每个全职员工必须连续工作 5 天,然后休息 2 天。例如,在周一到周五工作的员工必须在周六和周日休息。邮局希望只雇佣全职员工来满足自己的日常需求,并且雇佣员工的人数要最少。下面我们对这个问题的一些重要信息进行一下总结: 每个全职工人只能连续工作 5 天,然后就要休息 2 天 第一天(周一):需要 17 个工人 第二天:需要 13 个工人 第三天:需要 15 个工人 第四天:需要 19 个工人 第五天:需要 14 个工人 第六天:需要 16 个工人 第七天(周日):需要 11

42、 个工人 邮局需要对满足自己需要的雇员数目实现最小化。 回页首建模下面让我们开始分析这个问题的决策变量。我们应该使用 7 个变量,一周中的每天都要使用一个变量,其值等于在当天工作的员工数目。尽管乍看起来这已经解决了这个问题,但是这不能实现一个员工每周只能工作 5 天的约束,因为在员工某天工作并不能要求该员工在下一天也工作。 正确的途径应该是确保在 i天开始工作的员工在接下来的连续 4 天也会工作,因此正确的方法是使用 x i表示从 i天开始工作的员工数目。使用这种方法,强制这种连续约束就简单多了。决策变量就变成了: x1:从周一开始工作的员工数目 x2:从周二开始工作的员工数目 x3:从周三开

43、始工作的员工数目 x4:从周四开始工作的员工数目 x5:从周五开始工作的员工数目 x6:从周六开始工作的员工数目 x7:从周日开始工作的员工数目 需要最小化的目标函数是所雇佣员工的数量,它可以这样给出: 那么,约束都是什么呢?一周中的每天都有一个约束,这是为了确保当天的工人数量最少。让我们以周一为例来看一下。哪些人在周一工作呢?在我脑海中浮 现出来的第一个(片面)答案是 “那些在周一开始工作的人”。但是还有别人吗?有,那些要连续工作 5 天的员工中,周日开始工作的员工在周一时应该也在工作(回想一下问题的定义)。同理,我们可以推论那些周六、周五、周四开始工作的员工在周一也都在工作。 这个约束确保

44、周一至少有 17 名员工在工作。 类似地: 回页首当然,不要忘记了符号约束: 邮局问题的 GNU MathProg 解决方案注意:清单 4 中的行号仅仅是为了参考方便而给出的。 清单 4. 邮局问题解决方案 1 #2 # Post office problem3 #4 # This finds the optimal solution for minimizing the number of full-time5 # employees to the post office problem6 #78 /* sets */9 set DAYS;1011 /* parameters */12 pa

45、ram Need i in DAYS;1314 /* Decision variables. xi: No. of workers starting at day i */15 var x i in DAYS = 0;1617 /* objective function */18 minimize z: sumi in DAYS xi;1920 /* Constraints */2122 s.t. mon: sumi in DAYS: iWed xi = NeedMon;23 s.t. tue: sumi in DAYS: iThu xi = NeedTue;24 s.t. wed: sumi

46、 in DAYS: iFri xi = 回页首NeedWed;25 s.t. thu: sumi in DAYS: iSat xi = NeedThu;26 s.t. fri: sumi in DAYS: iSun xi = NeedFri;27 s.t. sat: sumi in DAYS: iMon xi = NeedSat;28 s.t. sun: sumi in DAYS: iTue xi = NeedSun;2930 data;3132 set DAYS:= Mon Tue Wed Thu Fri Sat Sun;3334 param Need:=35 Mon 1736 Tue 1337 Wed 1538 Thu 1939 Fri 1440

展开阅读全文
相关资源
猜你喜欢
相关搜索
资源标签

当前位置:首页 > 高等教育 > 理学

本站链接:文库   一言   我酷   合作


客服QQ:2549714901微博号:道客多多官方知乎号:道客多多

经营许可证编号: 粤ICP备2021046453号世界地图

道客多多©版权所有2020-2025营业执照举报