Presentation is loading. Please wait.

Presentation is loading. Please wait.

常用数学软件选讲.

Similar presentations


Presentation on theme: "常用数学软件选讲."— Presentation transcript:

1 常用数学软件选讲

2 第八章 Lingo软件及其应用 1. Lingo中的集 2. 模型的数据部分和初始部分 3. Lingo函数
4. Lingo Windows命令 5. Lingo与电子制表软件的连接 6. Lingo与数据库的连接 7. Lingo与Visual C++的连接 8. 利用Lingo开发高级模型

3

4 2 Lingo中的集 对实际问题建模的时候,总会遇到一群或多群相联系的对象,比如工厂、消费者群体、交通工具和雇工等等。Lingo允许把这些相联系的对象聚合成集(sets)。一旦把对象聚合成集,就可以利用集来最大限度的发挥Lingo建模语言的优势。 2.1 为什么使用集 2.2 什么是集 2.3 模型的集部分 2.4 小结

5 2.1 为什么使用集 集是Lingo建模语言的基础,是程序设计最强有力的基本构件。借助于集,能够用一个单一的、长的、简明的复合公式表示一系列相似的约束,从而可以快速方便地表达规模较大的模型。 例如:对于100个货栈的运输问题,如果一个一个的写出全部约束将是可怕的工作量。 货栈1的运量<=存量 货栈2的运量<=存量 货栈3的运量<=存量 ……………………………… Lingo可以采用最为简洁的表示方法: 每个货栈的运输量<=存量

6 2.2 什么是集 集是一群相联系的对象,这些对象也称为集的成员。一个集可能是一系列产品、卡车或雇员。每个集成员可能有一个或多个与之有关联的特征,我们把这些特征称为属性。属性值可以预先给定,也可以是未知的,有待于Lingo求解。 例如: (1)产品集中的每个产品可以有一个价格属性; (2)卡车集中的每辆卡车可以有一个牵引力属性; (3)雇员集中的每位雇员可以有一个薪水属性,也可以有一个生日属性等等。

7 2 Lingo中的集 2.2 什么是集(续) Lingo有两种类型的集:
原始集(primitive set)和派生集(derived set) 一个原始集是由一些最基本的对象组成的。 例如:集合WAREHOUSE是有6个货栈组成 集合VENDERS是由8个销售商组成

8 2 Lingo中的集 2.2 什么是集(续) 一个派生集是用一个或多个其它集来定义的,也就是说,它的成员来自于其它已存在的
例如:由6个货栈和8个销售商之间的联系而形成的集合(LINKS)就是派生集合,需要注意的是,派生集合也可以由其他派生集合生成

9 2.3 模型的集部分 集部分是Lingo模型的一个可选部分。 在Lingo模型中使用集之前,集部分必须事先定义。
集部分以关键字“sets:”开始,以“endsets”结束。 一个模型可以没有集部分,或有一个简单的集部分,或有多个集部分。 一个集部分可以放置于模型的任何地方,但是一个集及其属性在模型约束中被引用之前必须定义了它们。

10 2.3 模型的集部分 Set1集合定义了两个属性x和y。x取1、2、3三个值,而y取4、5、6三个值。
sets: set1/1..3/:x,y; endsets Set1集合定义了两个属性x和y。x取1、2、3三个值,而y取4、5、6三个值。 data: x=1 2 3; y=4 5 6; enddata sets: set1/1..3/:x,y; endsets Set1集合定义了两个属性x和y。x取1、2、3三个值,而y取4、5、6三个值。 data: x y = 1 4 2 5 3 6; enddata

11 2.3 模型的集部分 sets: warehouses/wh1..wh6/: capacity;
vendors/v1..v8/: demand; links(warehouses,vendors): cost, volume; endsets data: capacity= ; demand= ; cost= ; enddata

12 2 Lingo中的集 2.3.1 定义原始集 定义原始集的语法 集的名字[/集的成员/][:集成员的属性];
注意:用“[]”表示该部分内容可选。 如果集成员放在集定义中,那么对它们可采取显式罗列和隐式罗列两种方式。 如果集成员不放在集定义中,那么可以在随后的数据部分定义它们。

13 2 Lingo中的集 2.3.1 定义原始集(续) ① 当显式罗列成员时,必须为每个成员输入一个不同的名字,中间用空格或逗号搁开,允许混合使用。 例2.1 可以定义一个名为students的原始集,它具有成员John、Jill、Rose和Mike,属性有sex和age: sets: students/John Jill, Rose Mike/: sex, age; endsets

14 2 Lingo中的集 2.3.1 定义原始集(续) ② 当隐式罗列成员时,不必罗列出每个集成员。可采用如下语法:
隐式成员列表格式 示例 所产生集成员 1..n 1..5 1,2,3,4,5 StringM..StringN Car2..car14 Car2,Car3,Car4,…,Car14 DayM..DayN Mon..Fri Mon,Tue,Wed,Thu,Fri MonthM..MonthN Oct..Jan Oct,Nov,Dec,Jan MonthYearM..MonthYearN Oct2001..Jan2002 Oct2001,Nov2001,Dec2001,Jan2002

15 2 Lingo中的集 2.3.1 定义原始集(续) ③ 集成员不放在集定义中,而在随后的数据部分来定义。 !集部分; sets:
students:sex,age; endsets !数据部分; data: students,sex,age= John 1 16 Jill Rose 0 17 Mike ; enddata 注意:开头用感叹号(!),末尾用分号(;)表示注释,可跨多行。

16 2 Lingo中的集 原始集和C++语言的类比

17 2 Lingo中的集 2.3.2 定义派生集 定义派生集的语法 集的名字(父集名称列表)[/集的成员/][:集成员的属性];
注意:用“[]”表示该部分内容可选。 父集名称列表是已定义的集的列表,多个时必须用逗号隔开。如果没有指定成员列表,那么Lingo会自动创建父集成员的所有组合作为派生集的成员(参见下页的例子)。派生集的父集既可以是原始集,也可以是其它的派生集。

18 2 Lingo中的集 2.3.2 定义派生集(续) 定义派生集的例子 sets: product/A B/; machine/M N/;
week/1..2/; allowed(product,machine,week):x; endsets Lingo生成了三个父集的所有组合共八组作为allowed集的成员: 编号 成员 1 (A,M,1) 2 (A,M,2) 3 (A,N,1) 4 (A,N,2) 5 (B,M,1) 6 (B,M,2) 7 (B,N,1) 8 (B,N,2)

19 2 Lingo中的集 2.3.2 定义派生集(续) 稠密集的定义 稀疏集的定义 派生集成员列表方式
成员列表被忽略时,派生集成员由父集成员所有的组合构成,这样的派生集成为稠密集。 如果限制派生集的成员,使它成为父集成员所有组合构成的集合的一个子集,这样的派生集成为稀疏集。 ①显式罗列。 例子:allowed(product,machine,week)/A M 1,A N 2,B N 1/; ②设置成员资格过滤器。

20 2 Lingo中的集 设置成员资格过滤器 sets: !学生集:性别属性sex,1表示男性,0表示女性;年龄属性age. ;
students/John,Jill,Rose,Mike/:sex,age; !男学生和女学生的联系集:友好程度属性friend,[0,1]之间的数。 ; linkmf(students,students)|sex(&1) #eq# 1 #and# sex(&2) #eq# 0: friend; !男学生和女学生的友好程度大于0.5的集; linkmf2(linkmf) | friend(&1,&2) #ge# 0.5 : x; endsets data: sex,age = 1 16 0 14 0 17 0 13; friend = ; enddata

21 2 Lingo中的集 设置成员资格过滤器(续) 用竖线(|)来标记一个成员资格过滤器的开始。
#eq#是逻辑运算符,用来判断是否“相等”,可参考§4. &1可看作派生集的第1个原始父集的索引,它取遍该原始父集的所有成员; &2可看作派生集的第2 个原始父集的索引,它取遍该原始父集的所有成员;&3,&4,……,以此类推。 注意:如果派生集B的父集是另外的派生集A,那么上面所说的原始父集是集A向前回溯到最终的原始集,其顺序保持不变,并且派生集A的过滤器对派生集B仍然有效。 因此,派生集的索引个数是最终原始父集的个数,索引的取值是从原始父集到当前派生集所作限制的总和。

22 2 Lingo中的集 2.4 小结 Lingo可识别的集只有两种:原始集和派生集
派生集是由其它的集来创建。这些集被称为该派生集的父集(原始集或其它的派生集)。一个派生集既可以是稀疏的,也可以是稠密的。稠密集包含了父集成员的所有组合(有时也称为父集的笛卡尔乘积)。稀疏集仅包含了父集的笛卡尔乘积的一个子集,可通过显式罗列和成员资格过滤器这两种方式来定义。显式罗列方法就是逐个罗列稀疏集的成员。成员资格过滤器方法通过使用稀疏集成员必须满足的逻辑条件从稠密集成员中过滤出稀疏集的成员。

23 2 Lingo中的集 2.4 小结 Lingo集类型的示意

24 2 Lingo中的集上机作业 1、熟悉Lingo的安装 2、简单熟悉Lingo的语法规范,并可以解题

25 2 Lingo中的集上机作业 算例:某玻璃制造厂与三个不同地点的纯碱供应商签订合同,由他们供货给三个分厂,条件是不超过合同所定的数量,但必须满足生产需要。该问题如表3-1所示。问题中所给费率是每个供应商到每个工厂之间最短路径的运输费率。求运输方案

26 2 Lingo中的集上机作业 3-1运输问题-供需情况 工厂1 工厂2 工厂3 供应量 供应商1 x11 x12 x13 400 供应商2 x21 x22 x23 700 供应商3 x31 x32 x33 500 需求量 600 供销平衡

27 3 模型的数据部分和初始部分 3.1 模型的数据部分 3.2 模型的初始部分
在处理模型的数据时,需要为集指派一些成员并且在Lingo求解模型之前为集的某些属性指定值。为此,Lingo为用户提供了两个可选部分:输入集成员和数据的数据部分(Data Section)和为决策变量设置初始值的初始部分(Init Section)。 3.1 模型的数据部分 3.2 模型的初始部分

28 3 模型的数据部分和初始部分 3.1 模型的数据部分 为什么需要数据部分 数据部分入门
数据部分提供了模型相对静止部分和数据分离的可能性。显然,这对模型的维护和维数的缩放非常便利。 数据部分以关键字“data:”开始,以关键字“enddata”结束。在这里,可以指定集成员、集的属性。 其语法如下: 对象列 = 数值列;

29 数据部分入门(续) 对象列:包含要指定值的属性名
数值列:包含要分配给对象列中的对象的值,用逗号或空格隔开。注意属性值的个数必须等于集成员的个数。看下面的例子。 sets: set1/A,B,C/: X,Y; endsets data: X=1,2,3; Y=4,5,6; enddata 在集set1中定义了两个属性X和Y。X的三个值是1、2和3,Y的三个值是4、5和6。

30 数据部分入门(续) 也可采用如下例子中的复合数据声明(data statement): sets: set1/A,B,C/: X,Y;
endsets data: X,Y=1 4 2 5 3 6; enddata 看到这个例子,可能会认为X被指定了1、4和2三个值,因为它们是数值列中前三个,而正确的答案是1、2和3。假设对象列有n个对象,Lingo在为对象指定值时,首先在n个对象的第1个索引处依次分配数值列中的前n个对象,然后在n个对象的第2个索引处依次分配数值列中紧接着的n个对象,……,以此类推。 模型的所有数据——属性值和集成员——被单独放在数据部分,这可能是最规范的数据输入方式。

31 3.1 模型的数据部分 参数 在数据部分也可以指定一些标量变量(scalar variables)。当一个标量变量在数据部分确定时,称之为参数。 data: interest_rate = .085; enddata 模型中用利率8.5%作为一个参数 也可以同时指定多个参数。 data: interest_rate, inflation_rate = ; enddata

32 3.1 模型的数据部分 实时数据处理 在某些情况,对于模型中的某些数据并不是定值,我们把这种情况称为实时数据处理(what if analysis) data: interest_rate,inflation_rate = ?; enddata

33 3.1 模型的数据部分 指定属性为一个值 可以在数据声明的右边输入一个值来把所有的成员的该属性指定为一个值。 sets:
days /MO,TU,WE,TH,FR,SA,SU/:needs; endsets data: needs = 20; enddata Lingo将用20指定days集的所有成员的needs属性。

34 days /MO,TU,WE,TH,FR,SA,SU/:needs,cost; endsets data:
3.1 模型的数据部分 指定属性为一个值(续) 对于多个属性的情形, 也可以指定一个值 sets: days /MO,TU,WE,TH,FR,SA,SU/:needs,cost; endsets data: needs cost = ; enddata

35 3.1 模型的数据部分 数据部分的未知数值 有时只想为一个集的部分成员的某个属性指定值,而让其余成员的该属性保持未知,以便让Lingo去求出它们的最优值。在数据声明中输入两个相连的逗号表示该位置对应的集成员的属性值未知。两个逗号间可以有空格。 sets: years/1..5/: capacity; endsets data: capacity = ,34,20,,; enddata 属性capacity的第2个和第3个值分别为34和20,其余的未知。

36 3.2 模型的初始部分 模型的初始数值 初始部分是Lingo提供的一个可选部分。在初始部分中,可以输入初始声明(initialization statement)。初始部分输入的值仅被Lingo求解器当作初始点来用,并且仅仅对非线性模型有用。 一个初始部分以“init:”开始,以“endinit”结束。 init: X, Y = 0, .1; endinit X^2+Y^2<=1; 好的初始点会减少模型的求解时间(看迭代次数)。

37 4 Lingo函数 Lingo有9种类型的函数: 1. 基本运算符:包括算术运算符、逻辑运算符和关系运算符
1.  基本运算符:包括算术运算符、逻辑运算符和关系运算符 2.  数学函数:三角函数和常规的数学函数 3.  金融函数:Lingo提供的两种金融函数 4.  概率函数:Lingo提供了大量概率相关的函数 5.  变量界定函数:这类函数用来定义变量的取值范围 6.  集操作函数:这类函数为对集的操作提供帮助 7.  集循环函数:遍历集的元素,执行一定的操作的函数 8.  数据输入输出函数:这类函数允许模型和外部数据源相联系,进行数据的输入输出 9.  辅助函数:各种杂类函数

38 4.1 基本运算符 4.1.1 算数运算符 算术运算符是针对数值进行操作的。LINGO提供了5种二元运算符: ^ 乘方 ﹡ 乘 / 除
^ 乘方 ﹡ 乘 / 除 ﹢ 加 ﹣ 减 Lingo唯一的一元算术运算符是取反函数“﹣”。 算术运算符示例: 2﹣5/3,(2﹢4)/5等等。

39 4.1.2 逻辑运算符 在Lingo中,逻辑运算符主要用于集循环函数的条件表达式中,来控制在函数中哪些集成员被包含,哪些被排斥。在创建稀疏集时用在成员资格过滤器中。 (#not#  否定该操作数的逻辑值,#not#是一个一元运算符 ) 运算符 为TRUE时条件 #eq# 若两个运算数相等 #ne# 若两个运算符不相等 #gt# 若左边的运算符严格大于右边的运算符 #ge# 若左边的运算符大于或等于右边的运算符 #lt# 若左边的运算符严格小于右边的运算符 #le# 若左边的运算符小于或等于右边的运算符 #and# 仅当两个参数都为true时 #or#

40 #eq# #ne# #gt# #ge# #lt# #le# 低 #and# #or# 逻辑运算符示例:
4.1.2 逻辑运算符(续) 这些运算符的优先级由高到低为: 高 #not# #eq# #ne# #gt# #ge# #lt# #le# 低 #and# #or# 逻辑运算符示例: 2 #gt# 3 #and# 4 #gt# 2,其结果为假(0)。

41 (1)Lingo有三种关系运算符:“=”、“<=”和“>=”。
4.1.3 关系运算符 (1)Lingo有三种关系运算符:“=”、“<=”和“>=”。 (2)Lingo并不支持严格小于和严格大于关系运算符。 (3)Lingo中还能用“<”表示小于等于关系,“>”表示大于等于关系。 (4)如让A严格小于B,那么:A+ε<=B,

42 4.2 数学函数 Lingo提供了大量的标准数学函数: @abs(x) 返回x的绝对值 @sin(x) 返回x的正弦值,x采用弧度制
@cos(x) 返回x的余弦值 @tan(x) 返回x的正切值 @exp(x) 返回常数e的x次方 @log(x) 返回x的自然对数 @lgm(x) 返回x的gamma函数的自然对数 @sign(x) 如果x<0返回-1;否则,返回1 @floor(x) 返回x的整数部分。当x>=0时,返回不超过x的最大整数;当x<0时,返回不低于x的最大整数。 @smax(x1,x2,…,xn) 返回x1,x2,…,xn中的最大值 @smin(x1,x2,…,xn) 返回x1,x2,…,xn中的最小值

43 4.2 数学函数 - 模型实例 给定一个直角三角形,求包含该三角形的最小正方形。其中: 求最小的正方形就相当于求如下的最优化问题: C E
4.2 数学函数 - 模型实例 给定一个直角三角形,求包含该三角形的最小正方形。其中: 求最小的正方形就相当于求如下的最优化问题: A B C D E a b x

44 4.2数学函数 - 模型实例(续) Lingo代码如下: model: sets: object/1..3/: f; endsets
4.2数学函数 - 模型实例(续) Lingo代码如下: model: sets: object/1..3/: f; endsets data: a, b = 3, 4; !两个直角边长,修改很方便; enddata f(1) = a f(2) = b f(3) = a + b min @bnd(0,x,1.57); 限制0≤x≤1.57 end

45 Lingo代码如下: 50000 = x * @fpa(.0531,10); 答案是x=6573.069元。
4.3 金融函数 贷款金额50000元,贷款年利率5.31%,采取分期付款方式(每年年末还固定金额,直至还清)。问拟贷款10年,每年需偿还多少元? Lingo代码如下: 50000 = x 答案是x= 元。

46 4.3 金融函数 --函数解释 细心的同学可以发现这两个函数间的关系:

47 4.4 概率函数 1.@pbn(p,n,x) 二项分布的累积分布函数 当n和(或)x不是整数时,用线性插值法进行计算。
自由度为n的 分布的累积分布函数。 当到达负荷为a,服务系统有x个服务器且允许无穷排队时的Erlang繁忙概率。 当到达负荷为a,服务系统有x个服务器且不允许排队时的Erlang繁忙概率。 自由度为n和d的F分布的累积分布函数。 当负荷上限为a,顾客数为c,平行服务器数量为x时,有限源的Poisson服务系统的等待或返修顾客数的期望值。a是顾客数乘以平均服务时间,再除以平均返修时间。当c和(或)x不是整数时,采用线性插值进行计算。

48 4 Lingo函数 4.4 概率函数(续) 7.@phg(pop,g,n,x)
超几何(Hypergeometric)分布的累积分布函数。pop表示产品总数,g是正品数。从所有产品中任意取出n(n≤pop)件。pop,g,n和x都可以是非整数,这时采用线性插值进行计算。 Poisson分布的线性损失函数,即返回max(0,z-x)的期望值,其中随机变量z服从均值为a的Poisson分布。 均值为a的Poisson分布的累积分布函数。当x不是整数时,采用线性插值进行计算。 单位正态线性损失函数,即返回max(0,z-x)的期望值,其中随机变量z服从标准正态分布。 标准正态分布的累积分布函数。 自由度为n的t分布的累积分布函数。

49 4 Lingo函数 4.4 概率函数(续) 13.@qrand(seed)

50 4.5 变量界定函数 变量界定函数实现对变量取值范围的附加限制,共4种: @bin(x) 限制x为0或1
@bnd(L,x,U) 限制L≤x≤U @free(x) 取消对变量x的默认下界为0的限制,即x可以取任意实数 @gin(x) 限制x为整数

51 4.6 集处理函数 [,primitive_index_2,…]) 如果元素在指定集中,返回1;否则返回0。 全集为I,B是I的一个子集,C是B的补集。 sets: I/x1..x4/; B(I)/x2/; endsets

52 4.6 集处理函数 2.@index([set_name,] primitive_set_element)
该函数返回在集set_name中原始集成员primitive_set_element的索引。如果set_name被忽略,那么Lingo将返回与primitive_set_element匹配的第一个原始集成员的索引。如果找不到,则产生一个错误。 sets: !如何确定集成员(B,Y)属于派生集S3。 S1/A B C/; S2/X Y Z/; S3(S1,S2)/A X, A Z, B Y, C X/; endsets sets: girls/debble,sue,alice/; boys/bob,joe,sue,fred/; ! I1的值是2 ! I2的值是3

53 该函数返回j=index-k*limit,其中k是一个整数,取适当值保证j落在区间[1,limit]内。该函数相当于index模limit再加1。该函数在循环、多阶段计划编制中特别有用。(参见算例和职员分配模型) 类似C++取模函数:double j =fmod(index,limit);

54 该函数返回集set_name的成员个数。在模型中明确给出集大小时最好使用该函数。它的使用使模型更加数据中立,集大小改变时也更易维护。
该函数返回集set_name的成员个数。在模型中明确给出集大小时最好使用该函数。它的使用使模型更加数据中立,集大小改变时也更易维护。 sets: S1/A B C/; S2/X Y Z/; S3(S1,S2)/A X, A Z, B Y, C X/; S4(S1,S2); Endsets 用Lingo看看ABCD应该是多少?

55 4.7 集循环函数(重要) 集循环函数遍历整个集进行操作。其语法为 @function(setname[(set_index_list)[|conditional_qualifier]]: expression_list); setname是要遍历的集; set_ index_list是集索引列表;

56 4.7 集循环函数(续) 集循环函数遍历整个集进行操作。其语法为 @function(setname[(set_index_list)[|conditional_qualifier]]: expression_list);

57 例如产生序列(程序演示,产生什么序列) model: sets: number/1..5/:x; endsets @for(number(I): x(I)=I^2); end

58 2.@sum 该函数返回遍历指定的集成员的一个表达式的和。 例如:求向量[5,1,3,4,6,10]前5个数的和。 model: data:
N=6; enddata sets: number/1..N/:x; endsets x = ; | I #le# 5: x); end

59 3.@min和@max 返回指定的集成员的一个表达式的最小值或最大值。
例: 求向量[5,1,3,4,6,10]前5个数的最小值,后3个数的最大值。 model: data: N=6; enddata sets: number/1..N/:x; endsets x = ; | I #le# 5: x); | I #ge# N-2: x); end

60 如何构造对应的Lingo模型? 集循环函数的复杂例子
职员时序安排模型 一项工作一周7天都需要有人(比如护士工作),每天(周一至周日)所需的最少职员数为20、16、13、16、19、14和12,并要求每个职员一周连续工作5天(注意这里我们考虑稳定后的情况,不考虑临时工)。 在满足每天对职员需求的前提下,需要决定每天需要雇佣多少职员,以使一周所雇佣的总人数最少。 如何构造对应的Lingo模型? (1):相应的集合是什么?其属性又是什么? (2):哪个属性是数据及哪个属性是变量? (3):如何确定目标函数及约束条件?

61 相应的集合是什么?其属性又是什么? SETS: DAYS/MON TUE WED THU FRI SAT SUN/:
我们只有一个基本集合:一周的每一天。 SETS: DAYS/MON TUE WED THU FRI SAT SUN/: REQUIRED,START; ENDSETS DAYS的两个属性。第一个是每天对职员的需求,第二个是每天开始雇佣的职员的人数。分别记为REQUIRED和START。

62 哪个属性是数据及哪个属性是变量? REQUIRED 显然是给定的,所以它是数据; START 是我们要确定的,所以它是变量。
一旦确定了哪个属性是数据,就可以对数据进行赋值。 DATA: REQUIRED= ; ENDDATA

63 如何确定目标函数及约束条件? “每天当班的职员数”如何计算? MIN=@SUM(DAYS(I):START(I)); 确定目标函数:
约束条件:每天当班的职员数要大于等于当天对职员的需求数,对一周的每天都是如此。 “每天当班的职员数”如何计算?

64 “每天当班的职员数”如何计算? @FOR(DAYS(J): @SUM(DAYS(I)|I#LE#5:START(J-I+1))
为了计算出当天工作的职员人数,我们要求出当天开始雇佣的职员人数及前4天开始雇佣人数之和。5天前和6天前开始雇佣的人数不得记入。 转化成为Lingo语言: @FOR(DAYS(J): @SUM(DAYS(I)|I#LE#5:START(J-I+1)) >=REQUIRED(J)); 眼睛要睁大点,看清楚!

65 为什么会出错?

66 研究一下周四的情况,进行举例说明: 星期四在DAYS中的索引为4,将星期四那天工作的职员人数的约束写出来是: START(4-1+1)+START(4-2+1)+START(4-3+1)+ START(4-4+1)+START(4-5+1)>=REQUIRED(4); 化简后为: START(4)+START(3)+START(2) +START(1)+START(0)>=REQUIRED(4);

67 START(0)索引是越界的! --它就是@WRAP! 如何来完成这个任务? @FOR(DAYS(J):
对于小于等于零的任何索引都对应到一周的某一天。具体地说,0对应SUN(7);-1对应SAT(6)…… 如何来完成这个任务? @FOR(DAYS(J): @SUM(DAYS(I)|I#LE#5: >=REQUIRED(J));

68 最终的LINGO优化模型: SETS: !定义集合; DAYS / MON TUE WED THU FRI SAT SUN/:
REQUIRED, START; ENDSETS DATA:!给集合里的数据部分赋值; REQUIRED = ; ENDDATA MIN DAYS( I): START( I)); !给定目标函数; !给出约束条件:约束条件:每天当班的职员数要大于等于当天对职员的需求数; @FOR(DAYS(J): @SUM(DAYS(I)|I#LE#5: >=REQUIRED(J));

69 职员的雇佣计划: 星期 MON TUE WED THU FRI SAT SUN 开始雇佣人数 8 2 6 3
6 3 从求解报告的需求约束盈余看,每一天的松弛量都是零。这意味着职员人数都不超过需求,即每天工作的人数与需求正好相等(没有闲人) 。 如果采用人工排布会是什么样子?

70 配料模型: 在该模型中,人们将几种物品混合在一起制成多种产品。各种产品对各种物品都有一个最低的质量要求。在满足质量要求的前提下,如何确定产品的数量以获得最大利润。

71 CHESS食品公司销售4种品牌的坚果混合制品。它们的名字是Pawn(士兵)、Knight(武士)、Bishop(主教)和King(国王)。每一种产品对Peanuts(花生)和Cashews(腰果)有一定的比例要求。如下列出了各种产品每磅所含两种坚果的重量(单位:盎司),以及每种产品的销售价格。 Pawn Knight Bishop King Peanuts(oz.) 15 10 6 2 Cashews(oz.) 1 14 Selling Price 3 4 5 产品质量要求及单价

72 如何构造集合? 如何构造目标函数? 如何构造约束?
CHESS食品公司每天可以从供应商那里得到70磅的花生(Peanuts)和250磅的腰果(Cashews)。我们的问题:每天在不超过供应量的前提下,每种品牌的产品各生产多少?可使得总收益达到最大? 注意:1磅=16盎司=0.454千克,1盎司=28.375克 如何构造集合? 如何构造目标函数? 如何构造约束?

73 SETS:. NUTS/PEANUTS,CASHEWS/:SUPPLY;
SETS: NUTS/PEANUTS,CASHEWS/:SUPPLY; BRANDS/PAWN,KNIGHT,BISHOP,KING/:PRICE,PRODUCE; ENDSETS 集合NUTS有唯一一个属性SUPPLY,用于表示每天的坚果供应量(磅); 集合BRANDS有两个属性PRICE和PRODUCE。这里,PRICE表示每种产品的销售价格,PRODUCE是决策变量,表示每天每种产品应该生产多少。 此外,我们还需要一个集合。为了输入品牌公式,需要构造一个二维表格,构造一个NUTS和BRANDS的派生集合。 FORMULA(NUTS,BRANDS):OUNCES; 集合名称为FORMULA,它有唯一一个属性OUNCES。

74 定义数据域: DATA:. SUPPLY= 750 250; PRICE= 2 3 4 5;
定义数据域: DATA: SUPPLY= ; PRICE= ; OUNCES= ; ENDDATA 目标函数:总收益最大 @SUM(BRANDS(J): OUNCES(I,J)*PRODUCE(J)/16)<=SUPPLY(I)); 左边的求和除以16是将单位“盎司”转换为“磅”

75 SETS: NUTS / PEANUTS, CASHEWS/: SUPPLY; BRANDS / PAWN, KNIGHT, BISHOP, KING/:PRICE, PRODUCE; FORMULA( NUTS, BRANDS): OUNCES; ENDSETS DATA: SUPPLY = ; PRICE = ; OUNCES = ; ENDDATA MAX BRANDS( I):PRICE( I) * PRODUCE( I)); @FOR( NUTS( I): @SUM( BRANDS( J): OUNCES( I, J) * PRODUCE( J) / 16) <=SUPPLY( I));

76

77 用Lingo求解背包问题

78 构造集合 SETS: ITEMS / ANT_REPEL, BEER, BLANKET, BRATWURST, BROWNIES, FRISBEE, SALAD, WATERMELON/: INCLUDE, WEIGHT, RATING; ENDSETS 属性INCLUDE为一个0-1变量,说明该物品是否包含在背包中,用于野餐。 WEIGHT说明每一物品的重量,而RATING存储着该物品的指数值

79 MAX = @SUM( ITEMS: RATING * INCLUDE);
构造模型(目标函数) MAX ITEMS: RATING * INCLUDE); 在这里没有明确说明ITEMS的具体变量,此处是要对所有的ITEMS进行操作

80 SETS: ITEMS / ANT_REPEL, BEER, BLANKET, BRATWURST, BROWNIES, FRISBEE, SALAD, WATERMELON/: INCLUDE, WEIGHT, RATING; ENDSETS DATA: WEIGHT RATING = ; KNAPSACK_CAPACITY = 15; ENDDATA MAX ITEMS: RATING * INCLUDE); @SUM( ITEMS: WEIGHT * INCLUDE) <= KNAPSACK_CAPACITY; @FOR( INCLUDE));

81 PERT(工程评价与审查技术)模型: 我们将建立一个PERT模型,以确定在一个新产品的生产流程中的关键作业线路。PERT技术很简单,但是很实用。该技术源于20世纪60年代。当时主要是为了帮助管理人员跟踪大工程的进展。

82 Finalize Design(确定方案) 10 Forecast Demand(预测需求) 14
假设Wireless Widget公司计划向市场投放一种新产品--The Solar Widget。为了保证准时投放市场,Wireless Widget公司要对主要作业进行PERT分析,其目的是找出关键作业路线。位于关键路线上的作业必须按时完成,这样才能保证Wireless Widget公司的产品及时投放市场,各项作业及预计完成时间如下: Task(作业) Weeks(时间) Finalize Design(确定方案) 10 Forecast Demand(预测需求) 14 Survey Competition(调查竞争) 3 Set Price(确定价格) Schedule Production Run(确定生产时间) 7 Cost Out(成本核算) 4 Train Salesmen(训练销售人员)

83 产品作业优先关系

84 可以构造如下的集合: TASKS /DESIGN,FORECAST,SURVEY,PRICE,SCHEDULE,COSTOUT,TRAIN/ :TIME,ES,LS,SLACK; 对于集合的4个属性,它们的解释如下: TIME 完成作业的时间 ES 作业最早开始时间 LS 作业最迟开始时间 SLACK 作业LS和ES之间相差的时间

85 在模型中输入优先关系: PRED(TASK,TASK)/ DESIGN,FORECAST, DESIGN,SURVEY, FORECAST,PRICE, FORECAST,SCHEDULE, SURVEY,PRICE, SCHEDULE,COSTOUT, PRICE,TRAIN, COSTOUT,TRAIN/;

86 给出数据域部分的内容 DATA: TIME=10,14,3,3,7,4,10; ENDDATA 此时我们有三个属性需要计算: 最早开始(ES)、最迟开始(LS)和松弛时间(SLACK)。 关键计算ES、LS,SLACK是两者之差。

87 计算ES(最早开始时间): 对于一项作业来说,作业t的最早开始时间等于作业t的所有紧前作业的最早开始时间加上它自身完成时间之和的最大值。 @FOR(TASKS(J)|J#GT#1:

88 计算LS (最迟开始时间) : 对于一项作业来说,作业t的最迟开始时间等于作业t的所有紧后作业的最早开始时间与作业t自身完成时间之差的最小值。 @FOR(TASKS(I)|I#LT#LTASK: 最后一项作业没有紧后作业,忽略

89 计算SLACK(松弛时间): @FOR(TASKS(I):SLACK(I)=LS(I)-ES(I)); 对于作业1的开始时间可以取任意值,咱们取0。最后一项作业的最迟开始时间不用计算。对于最后一个作业,最迟开始时间和最早开始时间应该是一致的。 ES(1)=0; LS(7)=ES(7);

90 SETS: TASKS / DESIGN, FORECAST, SURVEY, PRICE, SCHEDULE, COSTOUT, TRAIN/: TIME, ES, LS, SLACK; PRED( TASKS, TASKS) / DESIGN,FORECAST, DESIGN,SURVEY, FORECAST,PRICE, FORECAST,SCHEDULE, SURVEY,PRICE, SCHEDULE,COSTOUT, PRICE,TRAIN, COSTOUT,TRAIN /; ENDSETS DATA: TIME = 10, 14, 3, 3, 7, 4, 10; ENDDATA @FOR( TASKS( J)| J #GT# 1: ES( J) PRED( I, J): ES( I) + TIME( I))); @FOR( TASKS( I)| I #LT# LTASK: LS( I) PRED( I, J): LS( J) - TIME( I));); @FOR( TASKS( I): SLACK( I) = LS( I) - ES( I)); ES( 1) = 0; LTASK TASKS); LS( LTASK) = ES( LTASK);

91 匹配模型(采用元素过滤器): 假设你是公司规划部经理,该部门有8个分析专家,正打算搬入新的办公地点。新办公地点共有4个办公室。你需要将专家配成4对,每一对分配一个新的办公室。基于以往的观察,有些专家在一起合作得很好,而有些在一起则不行。为了部门安宁,你也许想找出一种匹配,使得在一起的专家发生冲突的可能性最小。为了这一目的,要计算出匹配专家的不相容等级分。等级从1到10,某个专家匹配的等级分为1意味着两个专家合作得非常好。反之,等级分为10意味着两个专家不能一起共事。

92 由于专家I和专家J的匹配与专家J和专家I的匹配是一样的,所以我们只给出表中斜对角线上方的数据。
1 2 3 4 5 6 7 8 9 专家不相容等级分 由于专家I和专家J的匹配与专家J和专家I的匹配是一样的,所以我们只给出表中斜对角线上方的数据。 大家好好讨论如何当经理的问题!

93 构造8个专家构成的基本集合: ANALYSTS/1..8/; 最终含有匹配关系的派生集合 PAIRS(ANALYSTS,ANALYSTS); 由于这个集合里含有(I,J)、(J,I)以及(I,I)。需要去掉后面两个。 PAIRS(ANALYSTS,ANALYSTS)| &2#GT#&1; 通过过滤器,斜线上方的元素记入了集合PAIRS。

94 给集合PAIRS定义两个属性,第一个属性是匹配不相容等级分RATING;第二个属性表明专家I是否与专家J匹配MATCH。
PAIRS(ANALYSTS,ANALYSTS)| &2#GT#&1:RATING,MATCH; 我们用下面的数据域(由上面列出的不相容等级分)初始化属性RATING. DATA: RATING= 8 7 6 2 3 4;

95 我们让MATCH(I,J)取值为1来表示将专家I与专家J匹配,否则MATCH(I,J)取值为0。这样,属性MATCH就是模型的决策变量。我们的目的就是要使得所有最终匹配的不相容等级分的总和最小。
RATING(I,J)*MATCH(I,J)); 约束条件:对于每一个专家,他只与另外一个专家匹配。 @FOR(ANALYSTS(I): @SUM(PAIRS(J,K)|J#EQ#I#OR#K#EQ#I: MATCH(J,K))=1);

96 模型的特色

97 SETS: ANALYSTS / 1..8/; PAIRS( ANALYSTS, ANALYSTS) | &2 #GT# &1: RATING, MATCH; ENDSETS DATA: RATING = 2 3 4; ENDDATA MIN PAIRS( I, J): RATING( I, J) * MATCH( I, J)); @FOR( ANALYSTS( I): @SUM( PAIRS( J, K) | J #EQ# I #OR# K #EQ# I: MATCH( J, K)) = 1 ); @FOR( PAIRS( I, MATCH( I, J)));

98 MIN 9 MATCH( 1, 2) + 3 MATCH( 1, 3) + 4 MATCH( 1, 4) + 2 MATCH( 1, 5)
SUBJECT TO 2] MATCH( 1, 2) + MATCH( 1, 3) + MATCH( 1, 4) + MATCH( 1, 5) + MATCH( 1, 6) + MATCH( 1, 7) + MATCH( 1, 8) = 1 3] MATCH( 1, 2) + MATCH( 2, 3) + MATCH( 2, 4) + MATCH( 2, 5) + MATCH( 2, 6) + MATCH( 2, 7) + MATCH( 2, 8) = 1 4] MATCH( 1, 3) + MATCH( 2, 3) + MATCH( 3, 4) + MATCH( 3, 5) + MATCH( 3, 6) + MATCH( 3, 7) + MATCH( 3, 8) = 1 5] MATCH( 1, 4) + MATCH( 2, 4) + MATCH( 3, 4) + MATCH( 4, 5) + MATCH( 4, 6) + MATCH( 4, 7) + MATCH( 4, 8) = 1 6] MATCH( 1, 5) + MATCH( 2, 5) + MATCH( 3, 5) + MATCH( 4, 5) + MATCH( 5, 6) + MATCH( 5, 7) + MATCH( 5, 8) = 1 7] MATCH( 1, 6) + MATCH( 2, 6) + MATCH( 3, 6) + MATCH( 4, 6) + MATCH( 5, 6) + MATCH( 6, 7) + MATCH( 6, 8) = 1 8] MATCH( 1, 7) + MATCH( 2, 7) + MATCH( 3, 7) + MATCH( 4, 7) + MATCH( 5, 7) + MATCH( 6, 7) + MATCH( 7, 8) = 1 9] MATCH( 1, 8) + MATCH( 2, 8) + MATCH( 3, 8) + MATCH( 4, 8) + MATCH( 5, 8) + MATCH( 6, 8) + MATCH( 7, 8) = 1 END INTE MATCH( 1, 2) INTE MATCH( 1, 3) INTE MATCH( 1, 4) INTE MATCH( 1, 5) INTE MATCH( 1, 6) INTE MATCH( 1, 7) INTE MATCH( 1, 8) INTE MATCH( 2, 3) INTE MATCH( 2, 4) INTE MATCH( 2, 5) INTE MATCH( 2, 6) INTE MATCH( 2, 7) INTE MATCH( 2, 8) INTE MATCH( 3, 4) INTE MATCH( 3, 5) INTE MATCH( 3, 6) INTE MATCH( 3, 7) INTE MATCH( 3, 8) INTE MATCH( 4, 5) INTE MATCH( 4, 6) INTE MATCH( 4, 7) INTE MATCH( 4, 8) INTE MATCH( 5, 6) INTE MATCH( 5, 7) INTE MATCH( 5, 8) INTE MATCH( 6, 7) INTE MATCH( 6, 8) INTE MATCH( 7, 8) 展开后的模型

99 前面的解答告诉我们,最优匹配的不相容等级分是6分。 最优解匹配为:(1,6)、(2,7)、(3,8)、(4,5)
最终解的情况: 前面的解答告诉我们,最优匹配的不相容等级分是6分。 最优解匹配为:(1,6)、(2,7)、(3,8)、(4,5) 对集合函数的认识更加深刻?

100 4.7 输入和输出函数 输入和输出函数可以把模型和外部数据比如文本文件、数据库和电子表格等连接起来。

101 @FILE是一个接口函数,利用它可以从外部文本文件中读出数据,可以至于模型的任何位置。这样就可以将集合域和数据域里的数据合并到文本文件里。 例中有两处涉及到数据: 第一个地方是集部分的6个warehouses集成员和8个vendors集成员; 第二个地方是数据部分的capacity,demand和cost数据。

102 SETS: WAREHOUSES 'WIDGETS2.LDT')/: CAPACITY; VENDORS 'WIDGETS2.LDT')/ : DEMAND; LINKS( WAREHOUSES, VENDORS): COST, VOLUME; ENDSETS MIN LINKS( I, J): COST( I, J) * VOLUME( I, J)); @FOR( VENDORS( J): @SUM( WAREHOUSES( I): VOLUME( I, J)) = DEMAND( J)); @FOR( WAREHOUSES( I): @SUM( VENDORS( J): VOLUME( I, J)) <= CAPACITY( I)); DATA: CAPACITY 'WIDGETS2.LDT'); DEMAND 'WIDGETS2.LDT'); COST 'WIDGETS2.LDT'); ENDDATA

103 模型的所有数据来自于WIDGETS2.LDT文件。其内容如下:
WH1 WH2 WH3 WH4 WH5 WH6 ~ !warehouses成员; V1 V2 V3 V4 V5 V6 V7 V8 ~ !vendors成员; ~ !产量; ~ !销量;   !单位运输费用矩阵; 把记录结束标记(~)之间的数据文件部分称为记录。如果数据文件中没有记录结束标记,那么整个文件被看作单个记录。注意到除了记录结束标记外,模型的文本和数据同它们直接放在模型里是一样的。 文本文件的注释被忽略,在一个模型中最多可以调用16个文本数据文件

104 2.@text函数 该函数被用在数据部分用来把解输出至文本文件中。它可以输出集成员和集属性值。
我们把用接口函数产生输出的数据声明称为输出操作。输出操作仅当求解器求解完模型后才执行,执行次序取决于其在模型中出现的先后。

105 2.@text函数--计算例子 例1:@TEXT(‘RESULTS.TXT’)=X;
将变量X的值(可能是向量)送入文件RESULTS.TXT 在这个例子里,缺省了文件名,将使变量START的值出现在屏幕上

106 2.@text函数--计算例子 SETS: DAYS / MON TUE WED THU FRI SAT SUN/:
REQUIRED, START; ENDSETS DATA: REQUIRED = ; @TEXT( 'OUT.TXT') = START; ENDDATA MIN DAYS( I): START( I)); @FOR( DAYS( J): @SUM( DAYS( I) | I #LE# 5: J - I + 1, 7))) >= REQUIRED( J));

107 3.@ranged(variable_or_row_name)
例如,假设一个模型具有下面的数据域: DATA ENDDATA 当求解模型时,变量X的值和它的目标系数的允许减少量将写入文件OUTPUT.TXT之中。借助输出语句左边的函数,可以将输出发送到一个文件、电子表格、数据库和内存区域。

108 4.@rangeu(variable_or_row_name)
例如,假设一个模型具有下面的数据域: DATA ENDDATA 当求解模型时,变量X的值和它的目标系数的允许减少量将写入文件OUTPUT.TXT之中。借助输出语句左边的函数,可以将输出发送到一个文件、电子表格、数据库和内存区域。

109 5.@status() 返回Lingo求解模型结束后的状态: 0 Global Optimum(全局最优)
1 Infeasible(不可行) 2 Unbounded(无界) 3 Undetermined(不确定) 4 Feasible(可行) 5 Infeasible or Unbounded(通常需要关闭“预处理”选项后重新求解模型,以确定模型究竟是不可行还是无界) 6 Local Optimum(局部最优) 7 Locally Infeasible(局部不可行,尽管可行解可能存在,但是LINGO并没有找到一个) 8 Cutoff(目标函数的截断值被达到) 9 Numeric Error(求解器因在某约束中遇到无定义的算术运算而停止) 通常,如果返回值不是0、4或6时,那么解将不可信,几乎不能用。该函数仅被用在模型的数据部分来输出数据。

110 5.@status() (续) model: min=@sin(x); data: @text()=@status(); enddata
部分计算结果为: Local optimal solution found at iteration: Objective value: 6 Variable Value Reduced Cost X

111 6.@dual() 在一个输出语句里,@dual函数输出对偶变量(影子价格)的值或一行的值。例如,假设模型具有下面的数据域: DATA:
ENDDATA 当求解模型时,变量X的值和它的reduced cost将被写到文件C:\OUTPUT.TXT里。如果函数的参数是一个行名,则所有生成行的对偶价格连同行名将一并被输出。输出可以送到一个文件、电子表格、数据库和左边输出语句指定的区域。

112 4.8 辅助函数 @if函数将评价一个逻辑表达式logical_condition,如果为真,返回true_ result,否则返回false_result。

113 1.@if 函数的例子 model: min=fx+fy; fx=@if(x #gt# 0, 100,0)+2*x;
#gt# 0,60+3*y,2*y); x+y>=30; end

114 model: x=1; @warn('x是正数',x #gt# 0); end
如果逻辑条件logical_condition为真,则产生一个内容为’text’的信息框。 model: x=1; @warn('x是正数',x #gt# 0); end

115 在下面的例子里,如果用户输入一个负利率,则会显示“非法的利率”。
DATA:! 提示用户输入年利率(YRATE)、还款年数(YEARS)和借款总额(LUMP) 我们将计算月还款额; YRATE = ?; YEARS = ?; LUMP = ?; ENDDATA MONTHS = YEARS * 12; ! 总月数t; ( 1 + MRATE) ^ 12 = 1 + YRATE; ! 月利率; ! 求解月付款额; LUMP = PAYMENT MRATE, MONTHS); ! 如果输入利率为负值将给出警告; @WARN( '非法的利率', YRATE #LT# 0);

116 5 Lingo Windows命令 5.1 文件菜单(File Menu) 1:新建(New) 2:打开(Open) 3:保存(Save)
4:另存为(Save As) 5:关闭(Close) 6:打印(Print) 7:打印设置(Print Setup) 8:打印预览(Print Preview) 9:输出到日志文件(Log Output) 10:提交Lingo命令脚本文件(Take Commands ) 11:引入Lindo文件(Import Lindo File) 12:输出文件(Export File) 14:数据库用户信息(Database User Info)

117 Lingo命令脚本 在其他平台(非Windows)上的Lingo用户,使用Lingo都是通过在冒号后面输入行命令来实现的。 1:MODEL
使用MODEL命令可以开始输入一个新的模型进入Lingo。输入END表示模型结束 MODEL: MAX = 20 * X + 30 * Y; X <= 50; Y <= 60; X + 2 * Y <= 120; END SET TERSEO 1 GO DIVERT SOLU.TXT SOLUTION RVRT QUIT

118 2:TAKE 该命令有两个作用:1)读取保存在磁盘上的模型文件,2:执行命令脚本。其语法:TAKE[filename] 如果忽略了文件名,Lingo将给出提示。 (1) 例如,假设在文件夹d:\lingo\lingomod下有文件model.lng。则TAKE读取为: TAKE C:\LINGO\LINGOMOD\MODEL.LNG (2) 用TAKE命令来执行一个命令脚本: TAKE D:\LINGO\TEST.LTF

119 该命令可以打开一个文件,使Lingo的报告(Solution\Range)从屏幕上转到文件中,即在指定的文件中存放报告。
3:DIVERT 该命令可以打开一个文件,使Lingo的报告(Solution\Range)从屏幕上转到文件中,即在指定的文件中存放报告。 由于DIVERT命令产生的文件是文本格式,所以它可以被其他的诸如WORD等程序使用。 RVRT将关闭打开的 输出文件 MODEL: MAX = 20 * X + 30 * Y; X <= 50; Y <= 60; X + 2 * Y <= 120; END SET TERSEO 1 GO DIVERT SOLU.TXT SOLUTION RVRT QUIT

120 4:SAVE SAVE命令将当前模型保存到一个文件中 MODEL: MAX = 20 * X + 30 * Y;
END SAVE MYMODEL.LNG

121 可以编译并求解当前模型,当Lingo编译模型时,它产生一个内部的执行译文,运行后将产生解答。
当Lingo完成求解后,它将全部解答显示在屏幕上,如果不想要全部解答,可在GO命令前加TERSE命令。如果要把由GO命令产生的解答报告发送到一个文件里,在运行GO之前使用DIVERT。 MODEL: MAX = 20 * X + 30 * Y; X <= 50; Y <= 60; X + 2 * Y <= 120; END SET TERSEO 1 GO DIVERT SOLU.TXT SOLUTION RVRT QUIT

122 引入LINDO文件

123 数据库用户信息(Database User Info)
LINGO

124 5.2 编辑菜单(Edit Menu) 1:恢复(Undo) 2:剪切 (Cut) 3:复制(Copy) 4:粘贴(Paste)
5:粘贴特定(Paste Special) 6:全选(Select All) 7:匹配小括号(Match Parenthesis ) 8:粘贴函数(Paste Function)

125 5.3 Lingo菜单 1:求解模型(Slove) 2:求解结果 (Solution) 3:灵敏度分析(Range)
用该命令产生当前模型的灵敏性分析报告:研究当目标函数的费用系数和约束右端项在什么范围(此时假定其它系数不变)时,最优基保持不变。灵敏性分析是在求解模型时作出的,因此在求解模型时灵敏性分析是激活状态,但是默认是不激活的。为了激活灵敏性分析,运行LINGO|Options…,选择General Solver Tab, 在Dual Computations列表框中,选择Prices and Ranges选项。灵敏性分析耗费相当多的求解时间,因此当速度很关键时,就没有必要激活它。

126 灵敏度分析--例子 某家具公司制造书桌、餐桌和椅子,所用的资源有三种:木料、木工和漆工。生产数据如下表所示:
每个书桌 每个餐桌 每个椅子 现有资源总数 木料 8单位 6单位 1单位 48单位 漆工 4单位 2单位 1.5单位 20单位 木工 0.5单位 成本单价 60单位 30单位 若要求桌子的生产量不超过5件,如何安排三种产品的生产可使利润最大?

127 用DESKS、TABLES和CHAIRS分别表示三种产品的生产量,建立LP模型。
max=60*desks+30*tables+20*chairs; 8*desks+6*tables+chairs<=48; 4*desks+2*tables+1.5*chairs<=20; 2*desks+1.5*tables+.5*chairs<=8; tables<=5; 求解这个模型,并激活灵敏性分析。这时,查看报告窗口(Reports Window),可以看到下页的结果。

128

129 “Reduced Cost”列出最优单纯形表中判别数所在行的变量的系数,表示当变量有微小变动时, 目标函数的变化率。其中基变量的reduced cost值应为0, 对于非基变量 Xj, 相应的 reduced cost值表示当某个变量Xj 增加一个单位时目标函数减少的量( max型问题)。本例中:变量tables对应的reduced cost值为5,表示当非基变量tables的值从0变为 1时(此时假定其他非基变量保持不变,但为了满足约束条件,基变量显然会发生变化),最优的目标函数值 = = 275。

130 “DUAL PRICE”(对偶价格)表示当对应约束有微小变动时, 目标函数的变化率。输出结果中对应于每一个约束有一个对偶价格。 若其数值为p, 表示对应约束中不等式右端项若增加1 个单位,目标函数将增加p个单位(max型问题)。显然,如果在最优解处约束正好取等号(也就是“紧约束”,也称为有效约束或起作用约束),对偶价格值才可能不是0。本例中:第3、4行是紧约束,对应的对偶价格值为10,表示当紧约束 3) 4 DESKS + 2 TABLES CHAIRS <= 20 变为 3) 4 DESKS + 2 TABLES CHAIRS <= 21 时,目标函数值 = = 290。对第4行也类似。 对于非紧约束(如本例中第2、5行是非紧约束),DUAL PRICE 的值为0, 表示对应约束中不等式右端项的微小扰动不影响目标函数。有时, 通过分析DUAL PRICE, 也可对产生不可行问题的原因有所了解。

131 灵敏度分析的结果:

132 〔目标部分〕:在[60-4,60+20] = [56,80]范围变化时,最优基保持不变。由于此时约束没有变化(只是目标函数中某个费用系数发生变化),所以最优基保持不变的意思也就是最优解不变。
〔约束部分〕:第2行约束中右端项原来为48,当它在[48-24,48+∞] = [24,∞]范围变化时,最优基保持不变。 灵敏性分析结果表示的是最优基保持不变的系数范围。由此,也可以进一步确定当目标函数的费用系数和约束右端项发生小的变化时,最优基和最优解、最优值如何变化。

133 5.4 模型通常形式...(Generate...)
从LINGO菜单中选用“Generate...”命令或直接按Ctrl+G组合键可以创建当前模型的代数形式。

134 5.5 选项...(Options...) 从LINGO菜单中选用“Options...”命令、单击“Options...”按钮或直接按Ctrl+I组合键可以改变一些影响LINGO模型求解时的参数。

135

136

137

138

139

140

141

142

143

144

145

146

147

148

149

150

151

152

153

154

155

156

157

158 5.6 Picture 按照矩阵形式以图形方式给出的

159

160 5.7 窗口菜单(Windows Menu) 1. 命令行窗口(Open Command Window)
从窗口菜单中选用“Open Command Window”命令或直接按Ctrl+1可以打开LINGO的命令行窗口。在命令行窗口中可以获得命令行界面,在“:”提示符后可以输入LINGO的命令行命令。

161

162 5.7 窗口菜单(Windows Menu) 2. 状态窗口(Status Window) 从窗口菜单中选用“Status Window”命令或直接按Ctrl+2可以打开LINGO的求解状态窗口。 如果在编译期间没有表达错误,那么LINGO将调用适当的求解器来求解模型。当求解器开始运行时,它就会显示如下的求解器状态窗口(LINGO Solver Status)。

163 5.7 窗口菜单(Windows Menu)

164 LP QP ILP IQP PILP PIQP NLP INLP PINLP 模型类型分类: 线性规划 二次规划 整数线性规划 整数二次规划
纯整数线性规划 PIQP 纯整数二次规划 NLP 非线性规划 INLP 非线性整数规划 PINLP 纯非线性整数规划 当前解的状态 : Global Optimum, Local Optimum, Feasible, Infeasible(不可行), Unbounded(无界), Interrupted(中断), Undetermined(未确定)

165

166 6 Lingo与电子制表软件的连接 6.1 采用简单的Cut和Paste命令 直接选中数据将其“粘贴”入模型的数据部分

167 6 Lingo与电子制表软件的连接 6.1 采用简单的Cut和Paste命令 直接选中报告中的数据将其“粘贴”入WORD文档

168 6.2 从电子表格中输入数据

169 下面给出一些@OLE的例子(从Excel读数据):
优势:直接在内存中进行,不需要用任何中间文件。 用Excel文件\XLS\MYDATA.XLS中的MYPRICES域的值初始化PRICE。 用Excel文件SPECS.XLS中的COST域和CAPACITY域的值初始化COST和CAPACITY

170 下面给出一些@OLE的例子(写数据到Excel):
@OLE(‘C:\XLS\MYMODEL.XLS’,’SOLUTION’)=X; 将X的值送到Excel文件\MYMODEL.XLS中命名为SOLUTION的域中。 @OLE(‘ROUTING.XLS’)=USED,LOAD; 将USED和LOAD的值送入Excel文件ROUTING.XLS中的同名域中。

171 6.2.1

172 6.2.1 利用@OLE函数(运输算例) 定义域名如下: CAPACITY K5:K10 COST C5:J10
DEMAND C11:J11 在EXCEL中定义域名的方法: (1)选择区域 (2)选择插入| 名称| 定义命令: (3)输入域名; (4)单击“OK”按钮 当我们求解模型时,Lingo将装载Excel及对应的电子表格,然后从表中为CAPACITY、COST和DEMAND属性输入数据。

173 6.2.1

174 这样可以在Excel和Lingo之间建立一种客户服务关系。
6.3 利用OLE实现与Excel自动连接 将一个脚本放到Excel电子表格的某个域里,然后通过OLE自动操作技术将脚本传到Lingo。 这样可以在Excel和Lingo之间建立一种客户服务关系。 (采用职员分配模型进行说明)

175 第一步:在域C16:I16放置职员需求,起名Required 第二步:在域C18:I18起名START
第三步:在Excel中嵌入的Visual Basic宏: Dim LINGO As Object Sub Auto_Open() Set LINGO = CreateObject("LINGO.Document.4") End Sub Sub LINGOSolve() Dim iErr As Integer iErr = LINGO.RunScriptRange("MODEL") If (iErr > 0) Then MsgBox ("Unable to solve model") End If

176

177 嵌入Lingo模型到Excel中, 嵌入Excel表格到Lingo模型中 参见算例说明

178 评论 将Lingo建立模型的特点与电子表格管理数据的特点结合起来是很有吸引力的。

179 其它 尽管Lingo和Excel可以非常紧密的结合,但是Lindo公司还是推出了内嵌入Excel的产品——What’s Best!。
The World's Most Powerful Solver for Microsoft Excel What'sBest! offers unmatched solution speed, capacity and reliability. The internal linear, nonlinear and integer solvers utilize state-of-the-art techniques, and our dedication to ongoing development will ensure their continued superiority.

180

181 7 Lingo与数据库连接 电子制表软件善于管理小到中等规模的数据。一旦我们的模型要处理大规模的数据,首选的就是数据库管理系统DBMS。
Lingo可与任何DBMS进行连接。ODBC为DBMS定义了一个标准化接口。借助于它,Lingo可以访问任何ODBC支持的数据库。

182 Lingo为下列DBMS安装了ODBC驱动程序:
Access DBase Excel FoxPro Oracle Paradox SQL Server Text Files

183 7.1 利用@ODBC输入集合元素 无论是基本集合还是派生集合,都可以从一个ODBC数据源中输入。 在集合域里输入基本集合元素的语法:
Primitive_Set_name/ @ODBC([‘data_source’[,’table_name’[,’column_name’]]])/ 基本集合名(Primitive_Set_name)是正在定义的基本集合的名字。数据源(data_source)参数是用ODBC管理器注册过的ODBC数据源的名字。数据表名(table_name)参数是含有集合元素表(在数据源里)的名称。最后,列名(column_name)参数就是上面数据表的列名

184 语法的缺省规则 如果数据源参数被忽略了,则模型的标题取而代之; 如果数据表被忽略了,则基本集合的名字取而代之。 如果列名被忽略了,基本集合的名字取而代之。 只有在忽略列名参数的情况下才可以忽略数据表名参数;只有在数据表名参数和列名参数都被忽略的情况下才可以忽略数据源参数。

185 @ODBC的例子 TRUCKS’,’TRUCK_NAME’)/ 数据表TRUCKS是存放在ODBC数据源SHIPPING里的。Lingo将此表的TRUCK_NAME列里的元素输入到基本集合TRUCKS之中。 在这个例子中,数据表名和列名都被忽略了,所以Lingo用基本集合名STOCKS取而代之。因此,Lingo将表STOCKS里的STOCKS列中的元素输入到基本集合STOCKS之中,而表STOCKS是存放在ODBC数据源PORTFOLIO里的。

186 在PERT模型中输入基本集合元素 现在,我们将修改PERT模型以说明如何从一个Microsoft Access数据库里输入工程项目名称(作为集合元素)。修改后的模型如下(改动部分用红色显示)

187 SETS: TASKS 'PERTODBC', 'TASKS', 'TASKS')/: TIME, ES, LS, SLACK; PRED( TASKS, TASKS) / DESIGN,FORECAST, DESIGN,SURVEY, FORECAST,PRICE, FORECAST,SCHEDULE, SURVEY,PRICE, SCHEDULE,COSTOUT, PRICE,TRAIN, COSTOUT,TRAIN /; ENDSETS DATA: TIME = 10, 14, 3, 3, 7, 4, 10; ENDDATA @FOR( TASKS( J)| J #GT# 1: ES( J) PRED( I, J): ES( I) + TIME( I))); @FOR( TASKS( I)| I #LT# LTASK: LS( I) PRED( I, J): LS( J) - TIME( I));); @FOR( TASKS( I): SLACK( I) = LS( I) - ES( I)); ES( 1) = 0; LTASK TASKS); LS( LTASK) = ES( LTASK);

188 输入基本集合元素的语句是: TASKS 'PERTODBC', 'TASKS', 'TASKS')/: TIME, ES, LS, SLACK; 我们从ODBC数据域PERTODBC里取出集合TASKS的元素,而不是在模型里将它们列出。数据源PERTODBC中有TASKS表,表中有一列,名叫TASKS,我们就是从它那里获取集合TASKS的元素。

189 在一个集合域里输入一个派生集合元素的语法是: Derived_set_name(parent_set_1[,parent_set_2…]) ’column_name_1’[,’column_name_2’ …]]]])/ 派生集合名(derived_set_name)是要定义的派生集合名。父集合(parent_set_i)列出了形成派生集合的所有父集合。数据源(data_source)参数是用ODBC管理器注册过的数据源。数据表名(table_name_i)参数是一个含有集合元素名的表的名字。最后,这些表包含了由父集合派生来的集合元素。

190 例1:LINKS(WARHOUSE,VENDORS)
‘OPEN_LINKS’,’WH_SOURCE’, ‘VN_DEST’)/ 数据表OPEN_LINKS存在数据源TRANSPORTATION之中。Lingo用数据表OPEN_LINKS里的两列WH_SOURCE和VN_DEST形成派生集合LINKS。 例2:SOURCES(PRODUCT,PLANT,PERIOD) 在这个例子里表名和列名都被忽略了。所有,Lingo用派生集合名(SOURCES)来代替表名;父集合名(PRODUCT,PLANT和PERIOD)代替列名。所以,数据表SOURCES是在数据源PLANNING里。Lingo用数据表的三列PRODUCT、PLANT和PERIOD形成派生集合SOURCES。

191 在PERT模型中输入派生集合元素 在前面,我们说明了如何利用ODBC往一个PERT模型里输入作业。作业集合是一个基本集合。PERT模型里还有一个优先关系的派生集合。我们通过修改优先关系的派生集合来扩展一下PERT例子。 修改后的模型如下(改动部分用红色显示)

192 SETS: TASKS / @ODBC( 'PERTODBC', 'TASKS', 'TASKS')/: TIME, ES, LS, SLACK; PRED( TASKS, TASKS) / @ODBC( 'PERTODBC', 'PRECEDENCE', 'BEFORE', 'AFTER')/; ENDSETS DATA: TIME = 10, 14, 3, 3, 7, 4, 10; ENDDATA @FOR( TASKS( J)| J #GT# 1: ES( J) PRED( I, J): ES( I) + TIME( I))); @FOR( TASKS( I)| I #LT# LTASK: LS( I) PRED( I, J): LS( J) - TIME( I));); @FOR( TASKS( I): SLACK( I) = LS( I) - ES( I)); ES( 1) = 0; LTASK TASKS); LS( LTASK) = ES( LTASK);

193 输入派生集合元素的语句是: PRED(TASKS,TASKS)/ @ODBC( 'PERTODBC', ‘PRECEDENCE’, ‘BEFORE’,’AFTER’)/; 我们从ODBC数据源里取得集合PRED的元素,而不是直接将它们列在模型里。更具体一点,表PRECEDENCE是位于ODBC数据源PERTODBC之中,我们用数据表PRECEDENCE的两列BEFORE和AFTER形成派生集合PRED并输入元素。

194 属性列表(attribut_list)是要初始化的属性列表,用逗号隔开。集合属性必须都是定义在同一个集合上。数据源(data_source)参数是含有数据表的ODBC数据源。数据表名(table_name)参数是含有数据的数据表。最后,列名(column_name_i)是为属性列表中的第i个属性输入数据的列。

195 例1:SHIPPING_COST= @ODBC(‘TRANSPORTATION’, ‘LINKS’,’COST’); Lingo用数据表LINKS里的COST列初始化属性SHIPPING_COST,而表LINKS是存放在ODBC数据源TRANSPORTATION中。 例2:VOLUME,WEIGHT= @ODBC(‘TRUCKS’,’CAPACITY’); 这里的数据库列表被忽略了。在这种情况下,Lingo用属性名(VOLUME,WEIGHT)代替。因此,Lingo用表CAPACITY里的两列VOLUME和WEIGHT对属性VOLUME和WEIGHT进行初始化,而表CAPACITY是存放在ODBC数据源TRUCKS中。

196 在PERT模型中输入数据 SETS: TASKS / @ODBC( 'PERTODBC', 'TASKS', 'TASKS')/:
TIME, ES, LS, SLACK; PRED( TASKS, TASKS) / @ODBC( 'PERTODBC', 'PRECEDENCE', 'BEFORE', 'AFTER')/; ENDSETS DATA: TIME = @ODBC( 'PERTODBC', 'TASKS', 'TIME'); ENDDATA @FOR( TASKS( J)| J #GT# 1: ES( J) PRED( I, J): ES( I) + TIME( I))); @FOR( TASKS( I)| I #LT# LTASK: LS( I) PRED( I, J): LS( J) - TIME( I));); @FOR( TASKS( I): SLACK( I) = LS( I) - ES( I)); ES( 1) = 0; LTASK TASKS); LS( LTASK) = ES( LTASK);

197 @ODBC([‘data_source’[,’table_name’[,‘column_name_1’[,’column_name2’…]]]]) = Attribute_list 属性列表(attribut_list)是要输出的属性列表,用逗号分开。集合属性必须都是定义在同一个集合上。数据源(data_source)参数是将要接收属性值的数据源。数据表名(table_name)参数是数据源里的数据表,该表中含有接收数据的所有列。最后,列名(column_name_i)参数数据表中的某列,该列接收属性列表中的第i个属性的数值。

198 例1: @ODBC(‘TRANSPORTATION’,
‘LINKS’,’COST’)=VOLUME; Lingo将把属性VOLUME的值送到表LINKS中的VOLUME列,而表LINKS是在数据源TRANSPOTATION里。 @ODBC函数的所有参数都忽略了,数据源用模型的标题取代,定义属性的集合取代数据表,属性名取代列名。

199 在PERT模型中输出数据 SETS: TASKS / @ODBC( 'PERTODBC', 'TASKS', 'TASKS')/:
TIME, ES, LS, SLACK; PRED( TASKS, TASKS) / @ODBC( 'PERTODBC', 'PRECEDENCE', 'BEFORE', 'AFTER')/; ENDSETS DATA: TIME = @ODBC( 'PERTODBC', 'TASKS', 'TIME'); @ODBC( 'PERTODBC', 'TASKS', 'EARLIEST', 'LATEST') = ES, LS; ENDDATA @FOR( TASKS( J)| J #GT# 1: ES( J) PRED( I, J): ES( I) + TIME( I))); @FOR( TASKS( I)| I #LT# LTASK: LS( I) PRED( I, J): LS( J) - TIME( I));); @FOR( TASKS( I): SLACK( I) = LS( I) - ES( I)); ES( 1) = 0; LTASK TASKS); LS( LTASK) = ES( LTASK);

200 输出统计报告

201 TRANOLE的例子 (1)需要ODBC的连接(在控制面板中)。 (2)当数据库容量更换时,模型不需要修正(数据库容量比较大)

202 Lingo和其他应用程序的连接 Lingo可以和Visual C++、Visual Basic等软件开发中应用
尽管Lingo是一个交互性的模型构建环境,但是也可以利用Lingo提供的一些功能嵌入自己构造的应用程序中。Lingo允许采用动态连接库, (DLL) 的方式让外部的应用程序调用它的功能。 这样其它的应用程序可以构造前台程序,而和模型相关的复杂问题交给Lingo在后台运算,这一点对最终的用户是透明的。

203 Lingo的动态连接库 Lingo的动态连接库是一个32位的DLL,因此可以运行在Windows95及以后的版本中。
在这里将演示采用Visual C/C++ and Visual Basic 等如何使用动态连接库调用Lingo的。而其它开发工具如何调用,可以参照这些例子完成。

204 Lingo的动态连接库的例子 采用职员分配的例子进行解释

205 Lingo模型的修改 MODEL: SETS:
DAYS / MON TUE WED THU FRI SAT SUN/:NEEDS, START, ONDUTY; ENDSETS [OBJECTIVE] MIN DAYS( I): START( I)); @FOR( DAYS( TODAY): ! 计算每天的值日人员数量; ONDUTY( TODAY) = @SUM( DAYS( D)| D #LE# 5: TODAY - D + DAYS)))); ! 需要的人员数量; ONDUTY( TODAY) >= NEEDS( TODAY); @GIN( START); ); DATA: NEEDS 1); @POINTER( 2) = START; @POINTER( 3) = ONDUTY; @POINTER( 4) = OBJECTIVE; @POINTER( 5) ENDDATA END Lingo模型的修改

206 数据域中的 @POINTER函数的功能是在Lingo求解器与调用的应用程序之间建立起直接的内存连接 。这样的基于内存的数据传输将是高效快捷的,避免了基于磁盘文件的数据传输
( n) 函数仅在外部程序通过DLL接口方式访问模型时在模型的内部使用。 NEEDS 1); 反之,则是将模型内部的数据传递到外部程序中。 @POINTER函数采用双精度浮点指针格式读写所有的数据。 BASIC 以及 C/C++ 程序员可以采用 double 数据类型,而FORTRAN程序员 采用的数据类型为 REAL*8 或者 DOUBLE PRECISION.

207 由于LINGO 。如果程序员没有分配足够的内存空间,将导致内存保护错误,或者出现没有警告的计算错误。 DATA: NEEDS 1); @POINTER( 2) = START; @POINTER( 3) = ONDUTY; @POINTER( 4) = OBJECTIVE; @POINTER( 5) ENDDATA 注意START、ONDUTY等属性排列的先后顺序。

208 LINGO 的动态链接库将输出若干个函数,它们在库文件LINGO8\DLL\LINGOD80
LINGO 的动态链接库将输出若干个函数,它们在库文件LINGO8\DLL\LINGOD80.LIB中有列表,并可以将这些函数导入到Microsoft C或者FORTRAN中. Visual Basic用户可以通过在项目中包含模块文件导入 LINGO8\DLL\LINGOD80.BAS完成上述功能。 下面给出Lingo动态链接库的输出函数: (1):void LSclearPointersLng( pLSenvLINGO pL) 参数:pL 指向LScreateEnvLng()函数建立的内存区域 (2): int LScloseLogFileLng( pLSenvLINGO pL) 关闭由LSopenLogFileLng()函数打开的Lingo创建的LOG文件 参数:指向LScreateEnvLng()函数建立的内存区域 返回值:没有错误返回0,非零表示出错。

209 (3):pLSenvLINGO CALLTYPE LScreateEnvLng();
创建Lingo的内存环境对象。所有的Lingo例程都需要一个合法的指向内存环境对象的指针。 返回值:返回0表示出错,否则返回对象地址。 (4):int LSdeleteEnvLng( pLSenvLINGO pL) 删除先前创建的Lingo环境对象,释放Lingo对象申请的系统内存。 参数:pL 指向LScreateEnvLng()函数建立的内存区域 返回值:没有错误返回0,非零表示出错。 (5):int LSexecuteScriptLng( pLSenvLINGO pL, char* pcScript) 该函数处理Lingo的命令行。 参数: pL 指向LScreateEnvLng()函数建立的内存区域 pcScript Lingo的命令行指针。 返回值:没有错误返回0,非零表示出错。

210 (6):int LSgetCallbackInfoLng( pLSenvLINGO pL, intnObject, void
(6):int LSgetCallbackInfoLng( pLSenvLINGO pL, intnObject, void* pResult) 可以在应用程序中建立一个函数,Lingo求解器会在固定的时间间隔调用该函数。这样可以知道Lingo的运行进程。这种功能的函数称为回调函数。 (7):int LSopenLogFileLng( pLSenvLINGO pL, char *pcLogFile 在进行模型处理时创建一个LOG文件。 (8):int LSsetCallbackErrorLng( pLSenvLINGO pL, lngCBFuncError_t pcbf, void* pUserData) 当出现错误时,Lingo调用该回调函数。 (9):int LSsetCallbackSolverLng( pLSenvLINGO pL, lngCBFuncError_t pcgf, void* pUserData) Lingo在运行的过程中将定期调用该函数。 Use this routine to specify a callback function that LINGO will call at frequent intervals when solving a model.

211 Lingo与Visual C++的连接 采用Visual C/C++ 6.0创建一个项目: 1. 用 File|New 命令打开一个对话框.
2. 选择MFC AppWizard(Exe) 3. 选择一个基于对话框的模板 4. 选中OLE Automation 按钮,OLE将支持Lingo的动态链接库功能 5. 选择下一步. 6. 按Finish 按钮.

212 使用Visual C++ 中的ClassWizard 将对话框中的成员变量等添加好
为了在项目中更好的应用Lingo的动态链接库,在菜单的Project|Add to Project|Files command and select the file将 \LINGO8\DLL\LINGD80.LIB 文件加入到项目中。 将Lingd80.h头文件添加到对话框类的文件头。 // staffDlg.h : header file #include "lingd80.h" #if !defined(AFX_STAFFDLG_H__74D746B7_CA4D_11D6_AC89_ D2AE__INCLUDED_) #define AFX_STAFFDLG_H__74D746B7_CA4D_11D6_AC89_ D2AE__INCLUDED_ #if _MSC_VER > 1000 #pragma once #endif // _MSC_VER > 1000 ///////////////////////////////////////////////////////////////////////////// // CStaffDlg dialog

213 Lingo与Visual C++的连接结果

214 Lingo与Visual Basic的连接
采用下面的步骤开始创建: 1. 采用 Visual Basic 的 File|NewProject 创建项目. 2. 选择 Standard Exe 并按 OK 按钮. 3. 构造上面的对话框。

215 Lingo与Visual Basic的连接
内容参见对应的文本文件

216 Lingo设计高级模型 1:确定性模型 确定最佳的工厂位置 2:随机性模型 最短路问题 飞行计划问题(来源于实际)
离散型随机变量的概率分布—二项分布 航空机票超订票问题 Erlang排队模型

217 用Lingo求解最短路线问题 1:背景 在最短路问题中,我们要在一个网络图上寻找从开始点到终点的最短距离。可以使用动态规划(DP)的方法解决这个问题。动态规划是将一个大而复杂的问题变成一系列小而易于处理的问题。通过求解这一系列小问题,得到整个问题的解答。动态规划最困难之处不是求解小问题涉及到的数学内容,而是为了分解问题而使用的一种递归式(动态规划基本方程)。 城市间的网络图

218 用Lingo求解最短路线问题 为了寻找网络的最短路线距离,我们将使用下面的动态规划递归式: F(i)是从节点i到终点的最短距离,D(i,j)是从节点i到节点j的距离。 具体说:从节点i到终点的最短距离是从节点i到临接点的距离加上邻接点的终点的最小距离之和的最小值

219 2:集合 只建立一个基本集合CITIES,它代表了网络里的城市。我们用这个基本集合派生出一个集合ROADS是一个疏集。 3:变量 定义在CITIES集合上的属性F为每一个城市到终点城市的最短距离。定义在ROADS集合上的属性D表示两个城市之间的直达距离。 4:公式 我们使用下面的语句计算各城市到城市10的最短距离: @FOR( CITIES( i)| i CITIES): F( i) ROADS( i, j): D( i, j) + F( j)) );

220 SETS: ! 我们要寻找一条从城市1到城市10的最短线路; ! 城市集合, F( i)表示从城市i到城市10的最短距离; CITIES /1..10/: F; !派生集合Roads表示两个城市之间的直通线路; ROADS( CITIES, CITIES)/ 1,2 1,3 1,4 2,5 2,6 2,7 3,5 3,6 3,7 4,5 4,6 5,8 5,9 6,8 6,9 7,8 7,9 8,10 9,10/: D; ! D( i, j) 表示城市i到城市j之间的直达距离; ENDSETS DATA: D = ; ENDDATA ! 城市10到城市10的距离为0; CITIES)) = 0; !城市i到城市10 的最短距离等于城市i到相邻城市j的距离加上城市j到 城市 10 的最短距离之和的最小值; @FOR( CITIES( i)| i CITIES): F( i) ROADS( i, j): D( i, j) + F( j)) );

221 用Lingo确定最佳的工厂位置 1:背景 工厂定位模型是运输问题的推广。工厂定位问题具有较大的决策范围,工厂位置是变量。厂商很可能会遇到这样一类问题:在满足顾客对产品要求的前提下使总运输成本达到最小。 2:问题 某公司计划增加一些产品加工点(工厂),有三个位置可供选择。现有四个客户需要该公司的产品且需求量已知。在每一个位置建立工厂都有与其关联的月运作费用,从工厂到客户的运输成本也不相同。此外,工厂的运输能力也是有限的,不得突破它的生产能力。现在需要决定哪些工厂要开工?开工的工厂给每一个客户运送多少产品使得总运输成本和工厂月运作费用之和最少。

222 3:集合 在这个模型里,我们建立了两个基本集合:一个是PLANTS(工厂)集合;一个是CUSTOMER(客户)集合。用这两个集合,我们建立了一个密集ARCS,它是由工厂集合与客户集合相乘而得。我们用个集合来表达工厂与客户之间的运输线路。 4:变量 在这个模型里,我们有两类决策变量。定义在集合ARCS上的属性VOL代表了工厂到客户每一个线路上的运输量;定义在集合PLANTS上的属性OPEN是用于表示哪些工厂将开工。具体地说,如果工厂p将开工,则OPEN(p)的值为1,否则为0。属性OPEN的元素将用下式定义为二元0/1变量:

223 5:目标函数 这个模型的目标函数是使得总成本(运输总成本加上运作成本的总和)达到最小。这可以用下面的表达式计算 @SUM(PLANTS:FCOST*OPEN); 6:约束条件 这个模型有两种类型的约束:第一种是每个客户必须得到足够的产品以满足需求;第二种是每个工厂的运输量不能超过它的生产量。 下面的表达式保证所有客户都可以获得足够的产品 @FOR(CUSTOMERS(J):[DEMAND] @SUM(PLANTS(I):VOL(I,J))>=DEM(J));

224 对每一个客户,我们计算出接收运输量的总和,并且让它大于或等于客户的需求量。为了限制工厂的运输量不超过它的生产能力:
@FOR(PLANTS(I):[SUPPLY] @SUM(CUSTOMERS(J):VOL(I,J))<= CAP(I)*OPEN(I)); 对于每一个客户,计算出了运输量的总和,并且让它小于或等于工厂的生产能力乘上表示工厂开工与否的指示器OPEN。

225 MODEL:! 工厂定位问题; SETS: PLANTS / P1, P2, P3/: FCOST, CAP, OPEN; CUSTOMERS / C1, C2, C3, C4/ : DEM; ARCS( PLANTS, CUSTOMERS) : COST, VOL; ENDSETS DATA: FCOST = 91, 70, 24; ! 每个工厂的月运作费用; CAP = 39, 35, 31; ! 每个工厂的生产能力; DEM = 15, 17, 22, 12; ! 每个客户的月需求; ! 单位产品的运输成本; COST = 6, 2, 6, 7, 4, 9, 5, 3, 8, 8, 1, 5; ENDDATA ! 目标函数; [TTL_COST] MIN ARCS: COST * VOL) + @SUM( PLANTS: FCOST * OPEN); !需求约束; @FOR( CUSTOMERS( J): [DEMAND] @SUM( PLANTS( I): VOL( I, J)) >= DEM( J) ); ! 供应约束; @FOR( PLANTS( I): [SUPPLY] @SUM( CUSTOMERS( J): VOL( I, J)) <= CAP( I) * OPEN( I) ); !限制OPEN为0/1变量; @FOR( OPEN)); END

226 (以第二次世界大战中的实例为背景,简化)
在甲乙双方的一场战争中,一部分甲方部队被乙方部队包围长达4个月。由于乙方封锁了所有水陆交通通道,被包围的甲方部队只能依靠空中交通维持供给。运送4个月的供给分别需要2,3,3,4次飞行,每次飞行编队由50架飞机组成(每架飞机需要3名飞行员),可以运送10万吨物资。每架飞机每个月只能飞行一次,每名飞行员每个月也只能飞行一次。在执行完运输任务后的返回途中有20%的飞机会被乙方部队击落,相应的飞行员也因此牺牲或失踪。 在第一个月开始时,甲方拥有110架飞机和330名熟练的飞行员。在每个月开始时,甲方可以招聘新飞行员和购买新飞机。新飞机必须经过一个月的检查后才可以投入使用,新飞行员必须在熟练飞行员的指导下经过一个月的训练才能投入飞行。每名熟练飞行员可以作为教练每个月指导20名飞行员(包括他自己在内)进行训练。每名飞行员在完成一个月的飞行任务后,必须有一个月的带薪假期,假期结束后才能再投入飞行。 各项费用如下表,请安排一个飞行计划

227 飞 行 计 划 的 原 始 数 据 第1月 第2月 第3月 第4月 新飞机价格 200.0 195.0 190.0 185.0 闲置的熟练飞行员报酬 7.0 6.9 6.8 6.7 教练和新飞行员报酬(含培训费用) 10.0 9.9 9.8 9.7 执行飞行任务的熟练飞行员报酬 9.0 8.9 休假期间的熟练飞行员报酬 5.0 4.9 4.8 4.7

228 定 义 决 策 变 量 设4个月开始时甲方新购买的飞机数量分别为x1,x2,x3,x4架,闲置的飞机数量分别为y1,y2,y3,y4架。4个月中,飞行员中教练和新飞行员数量分别为u1,u2,u3,u4人,闲置的熟练飞行员数量分别为v1,v2,v3,v4人。 目 标 函 数 Min=200*x1+195*x2+190*x3+185*x4+10*u1+9.9*u2+9.8*u3+9.7*u4+7*v1+6.9*v2+6.8*v3+6.7*v4;

229 约 束 条 件 (1)飞机数量限制。 4个月中执行飞行任务的飞机分别为100,150,150,200(架),但只有80,120,120,160(架)能够返回供下个月使用。 第一个月:100 + y1 = 110; 第二个月:150 + y2 = y1 + x1; 第三个月:150 + y3 = y2 + x2; 第四个月:200 + y4 = y3 + x3;

230 约 束 条 件 (2)飞行员数量限制。 4个月中执行飞行任务的熟练飞行员分别为300,450,450,600(人),但只有240,360,360,480(人)能够返回(下个月一定休假)。 第一个月: *u1+v1 = 330; 第二个月: *u2+v2 = u1+v1; 第三个月: *u3+v3 = u2+v2+240; 第四个月: *u4+v4 = u3+v3+360;

231 约 束 条 件 (参 见 具 体 的 程 序) (2)其它限制。 要求x1,x2,x3,x4, y1,y2,y3,y4,
约 束 条 件 (2)其它限制。 要求x1,x2,x3,x4, y1,y2,y3,y4, u1,u2,u3,u4, v1,v2,v3,v4>=0且全部为整数。 (参 见 具 体 的 程 序)

232 t =@pbn(0.2,20,4); 离散型随机变量的概率分布—二项分布 2:问题
Feasible solution found at iteration: Variable Value T

233 !x<=4时的概率; t4_all ; !x=0时的概率; t0 !x=1时的概率; t1 - t0; !x=2时的概率; t2 - t1-t0; !x=3时的概率; t3 - t2-t1-t0; !x=4时的概率; t4 -t3-t2-t1-t0;

234 航空机票超订票问题 某航空公司执行两地的飞行任务。已知飞机的有效载客量为150人。按民用航空管理有关规定:旅客因有事或误机,机票可免费改签一次,此外也可在飞机起飞前退票。航空公司为了避免由此发生的损失,采用超量订票的方法,即每班售出票数大于飞机载客数。但由此会发生持票登机旅客多于座位数的情况,在这种情况下,航空公司让超员旅客改乘其它航班,并给旅客票价的20%作为补偿。现假定两地的机票价格为1500元,每位旅客有0.04的概率发生有事、误机或退票的情况,问航空公司多售出多少张票使该公司的预期损失达到最小?

235 航空机票超订票问题——问题分析1

236 航空机票超订票问题——问题分析2

237 航空机票超订票问题——问题求解

238 航空机票超订票问题——求解程序 MODEL: DATA: N = 150; p = 0.04; k = 1500; h = 300;
ENDDATA @PBN(p,N+S,S) = k/(k+h); END 超定票数在8-9张之间,即每班售出的票数在 张

239 Erlang排队模型 1:背景 长期以来,电话,通信和计算机工业一直使用排队模型来估计面对随机需求时系统的性能。两个常见模型Erlang损失模型和Erlang等待模型。在前者中,任何发现所有服务器都在忙碌的顾客将会离开。而在后者中,任何发现所有服务器都在忙碌的顾客将会等待。 为了计算系统的性能,我们必须知道系统单位时间的负载和服务器的数量。负载是无单位的,表示单位时间里工作量的指标。例如:每小时20个顾客到达,每个顾客需要半小时,负载就为10,(20×0.5=10)

240 ! 每小时顾客的到达率; AR = 25; ! 每位顾客的服务时间(单位:分); STM = 6; ! 每位顾客的服务时间(单位:小时);
2:问题 假设你正管理一个顾客服务部。平均每小时会有25个顾客,接待一个顾客平均需要6分钟。如果要想使等待的顾客数不超过5%,那么,至少需要多少个服务器? (假设当所有服务器都忙碌的时候客户不可以等待) ! 每小时顾客的到达率; AR = 25; ! 每位顾客的服务时间(单位:分); STM = 6; ! 每位顾客的服务时间(单位:小时); STH = STM/ 60; ! 发现所有服务器都在忙碌的顾客比例; FB = .05; !利用PEL函数确定需要服务器的数量; FB AR * STH, NS);

241 Feasible solution found at iteration: 0
Variable Value AR STM STH FB E-01 NS Row Slack or Surplus

242 3:说明 由于服务器不能为小数,所以我们至少需要6个服务器。 4:问题进一步深入 假设当所有服务器都忙碌的时候客户可以等待。我们希望知道如下数据: (1):发现所有服务器都在忙碌的顾客比? (2):等待顾客的平均等待时间? (3):平均等待时间? (4):平均等待人数比?

243 ! 每小时顾客的到达率; AR = 25; ! 每位顾客的服务时间(单位:分); STM = 6; ! 每位顾客的服务时间(单位:小时); STH = STM/ 60; ! 服务器的数量; NS = 6; ! 利用PEB函数发现所有服务器都在忙碌的概率; FB AR * STH, NS); ! 等待顾客的平均等待时间; WAITC = 1 / ( NS / STH - AR); ! 所有顾客的平均等待时间; WAITU = FB * WAITC; ! 平均等待人数的百分比; NWAIT = AR * WAITU;

244 2:系统服务台(员)的最优确定 一个大型物流中心,正考虑修建物资卸位的个数。估计货车将按Poission流到达,平均每小时15车,卸货时间服从负指数分布,平均3分钟卸一辆。又知每辆运送货物的卡车8万元,修建一个卸位的投资是14万元。问:应修建多少个物资卸位最为适宜? 解决办法:可以用排队论中的M/M/S/∞进行分析(输入过程是Poission流,服务时间服从负指数分布,系统有S个服务台平行服务,系统容量为无穷的等待排队系统),其中费用包括连个方面,一个是建造物资卸位的费用,另一个是卡车处于排队状态下不能工作的费用,因此 目标函数为: Min = 14*S + 8*Ls

245 MODEL: R=15; T=3/60; LOAD = R*T;!负载; Pwait W_q = Pwait * T/(S-LOAD);!等待时间; W_s = W_q + T;!逗留时间; L_s = W_s * R;!等待队长; MIN = 8*L_s + 14*S; END

246 修建2个卸位,总成本是34.98万元 Local optimal solution found at iteration: 192
Objective value: Variable Value Reduced Cost R T E LOAD PWAIT S W_Q E W_S E L_S 修建2个卸位,总成本是34.98万元

247 E N D


Download ppt "常用数学软件选讲."

Similar presentations


Ads by Google