说明:
有了前几节的基础知识,再加上本节的内容,就能够借助于 LINGO
建立并求解复杂的优化模型。
函数类型( 9种):
1,基本运算符:算术运算符、逻辑运算符、关系运算符
2.数学函数:三角函数和常规的数学函数
3.金融函数:两种金融函数
4.概率函数:大量概率相关的函数
5.变量界定函数:定义变量的取值范围
6.集操作函数:对集的操作提供帮助
7.集循环函数:遍历集的元素,执行一定的操作的函数
8.数据输入输出函数:允许模型和外部数据源相联系,
进行数据的输入输出
9,辅助函数:各种杂类函数
§ 4 LINGO 函数
4.1 基本运算符
4.1.1 算术运算符二元运算符(五类):^乘方,﹡ 乘、/除,﹢ 加,﹣ 减取反函数,﹣ (唯一的一元算术运算符)
优先级由高到底:
高 ﹣ (取反)

﹡ /
低 ﹢ ﹣
运算次序:从左到右按优先级高低执行。
运算的序可用圆括号“()”改变。
例 4.1 算术运算符示例。
2﹣5 / 3,(2﹢4) / 5等等。
说明:
主要用于集循环函数的条件表达式中,控制在函数中哪些集成员被包含,
哪些被排斥。在创建稀疏集时用在成员资格过滤器中。
9种逻辑运算符:
#not# 否定该操作数的逻辑值,一元运算符
#eq# 若两个运算数相等,为 true;否则为 flase
#ne# 若两个运算符不相等,为 true;否则为 flase
#gt# 若左边运算符严格大于右边运算符,为 true;否则为 flase
#ge# 若左边运算符大于或等于右边的运算符,为 true;否则为 flase
#lt# 若左边运算符严格小于右边运算符,为 true;否则为 flase
#le# 若左边运算符小于或等于右边运算符,为 true;否则为 flase
#and# 仅当两个参数都为 true时,结果为 true;否则为 flase
#or# 仅当两个参数都为 false时,结果为 false;否则为 true
运算符的优先级由高到低:
高 #not#
#eq# #ne# #gt# #ge# #lt# #le#
低 #and# #or#
例 4.2 逻辑运算符示例 2 #gt# 3 #and# 4 #gt# 2,结果为假( 0)。
4.1.2 逻辑运算符说明,主要用在模型中,指定一个表达式的左边是否等于、小于等于、
或大于等于右边,形成模型的一个约束条件。
关系运算符与逻辑运算符 #eq#,#le#,#ge#区别:截然不同,前者是模型中该关系运算符所指定关系的为真描述,后者仅仅判断一个该关系是否被满足:满足为真,不满足为假。
关系运算符(三种):,=”,,<=”,,>=”。
说明:还能用,<” 表示小于等于关系,,>” 表示大于等于关系。
不支持严格小于和严格大于关系运算符。
若需要严格小于和严格大于关系,比如让 A严格小于 B,A<B,
则可把它变成如下的小于等于表达式:
A+ε <=B,ε 是一个小的正数,其值依赖于模型中 A小于 B多少才算不等。
三类操作符的优先级:
高 #not# ﹣ (取反)

﹡ /
﹢ ﹣
#eq# #ne# #gt# #ge# #lt# #le#
#and# #or#
低 <= = >=
4.1.3 关系运算符标准数学函数:
@abs(x),返回 x的绝对值
@sin(x),返回 x的正弦值,x采用弧度制
@cos(x),返回 x的余弦值
@tan(x),返回 x的正切值
@exp(x),返回常数 e的 x次方
@log(x),返回 x的自然对数
@lgm(x),返回 x的 gamma函数的自然对数
(即( x-1)!的自然对数)
@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中的最小值
4.2 数学函数求最小的正方形相当于求如下的最优化问题:
例 4.3 给定一个直角三角形,求包含该三角形的最小正方形解,如图所示。

0 2
m in m a x,,
x
CE AD D E

s i nC E a x? c o sA D b x? c o s s inD E a x b x
EC
DA
Bb
x
a
代码:
model:
sets:
object/1..3/,f;
endsets
data:
a,b = 3,4; !两个直角边长,修改很方便 ;
enddata
f(1) = a * @sin(x);
f(2) = b * @cos(x);
f(3) = a * @cos(x) + b * @sin(x);
min = @smax(f(1),f(2),f(3));
@bnd(0,x,1.57);
end
函数 @bnd,详情见 4.5节。
目前 LINGO提供了两个金融函数。
1,@fpa(I,n)
返回如下情形的净现值:单位时段利率为 I,连续 n个时段支付,每个时段支付单位费用。若每个时段支付 x单位的费用,则净现值可用 x乘以 @fpa(I,n)算得。计算公式:
即在一定时期内为了获得一定收益在该时期初所支付的实际费用。
例 4.4 贷款买房问题贷款金额 50000元,贷款年利率 5.31%,采取分期付款方式(每年年末还固定金额,直至还清)。问拟贷款 10年,每年需偿还多少元?
代码:
50000 = x * @fpa(.0531,10);
答案,x=6573.069元。
4.3 金融函数


1
111
1
nn
k
k
I
II

2,@fpl(I,n)
返回如下情形净现值:单位时段利率为 I,第 n个时段支付单位费用计算公式:
两个函数间的关系:
1 nI

1
@,@,
n
k
f p a I n f p l I k

1,@pbn(p,n,x),二项分布的分布函数。 n和(或) x不是整数时,
用线性插值法进行计算。
2,@pcx(n,x),自由度为 n的 χ2分布的分布函数。
3,@peb(a,x),当到达负荷为 a,服务系统有 x个服务器且允许无穷排队时的 Erlang繁忙概率。
4,@pel(a,x),当到达负荷为 a,服务系统有 x个服务器且不允许排队时的 Erlang繁忙概率。
5,@pfd(n,d,x),自由度为 n和 d的 F分布的分布函数。
*6,@pfs(a,x,c),当负荷上限为 a,顾客数为 c,平行服务器数量为 x时,有限源的 Poisson服务系统的等待或返修顾客数的期望值。 a
是顾客数乘以平均服务时间,再除以平均返修时间。当 c和(或) x
不是整数时,采用线性插值进行计算。
7,@phg(pop,g,n,x),超几何( Hyper geometric)分布的分布函数
。 pop是产品总数,g是正品数。从所有产品中任意取出 n( n≤pop)
件。 pop,g,n和 x都可以是非整数,采用线性插值进行计算。
4.4 概率函数
8,@ppl(a,x),Poisson分布的线性损失函数,即返回 max(0,z-x)的期望值
,其中随机变量 z服从均值为 a的 Poisson分布。
9,@pps(a,x),均值为 a的 Poisson分布的分布函数。 x不是整数时,采用线性插值进行计算。
10,@psl(x),单位正态线性损失函数,即返回 max(0,z-x)的期望值,随机变量 z服从标准正态分布。
11,@psn(x),标准正态分布的分布函数。
12,@ptd(n,x),自由度为 n的 t分布的分布函数。
13,@qrand(seed),产生服从 (0,1)区间的拟随机数。
@qrand只允许在模型的数据部分使用,它将用拟随机数填满集属性。通常,声明一个 m× n的二维表,m表示运行实验的次数,n表示每次实验所需的随机数的个数。在行内,随机数是独立分布的;在行间,随机数是非常均匀的。这些随机数是用“分层取样”的方法产生的。
例 4.5
model:
data:
M=4; N=2; seed=1234567;
enddata
sets:
rows/1..M/;
cols/1..N/;
table(rows,cols),x;
endsets
data:
X=@qrand(seed);
enddata
end
如果没有为函数指定种子,那么将用系统时间构造种子。
例 4.6 利用 @rand产生 15个标准正态分布随机数和自由度为 2的 t分布的随机数。
model:
!产生一列正态分布和 t分布的随机数 ;
sets:
series/1..15/,u,znorm,zt;
endsets
!第一个均匀分布随机数是任意的 ;
u(1)= @rand(,1234);
!产生其余的均匀分布的随机数 ;
@for(series(I)|I #GT# 1:u(I)=@rand(u(I-1)));
!产生正态分布随机数 ;
!和自由度为 2的 t分布随机数 ;
!ZNORM 和 ZT 可以是负数 ;
@for(series( I):@psn(znorm(I))=u(I);
@ptd(2,zt(I))=u(I);
@free(znorm(I));@free(zt(I)););
end
14,@rand(seed),返回 0和 1间的伪随机数,依赖于指定的种子。
典型用法,U(I+1)=@rand(U(I))。
注意如果 seed不变,那么产生的随机数也不变。
实现对变量取值范围的附加限制:
@bin(x) 限制 x为 0或 1
@bnd(L,x,U) 限制 L≤x≤U
@free(x) 取消对变量 x的默认下界为 0的限制,
即 x可取任意实数
@gin(x) 限制 x为整数
4.5 变量界定函数( 4种)
帮助处理集。
1,@in(set_name,primitive_index_1 [,primitive_index_2,… ])
如果元素在指定集中,返回 1;否则返回 0。
例 4.7 全集为 I,B是 I的一个子集,C是 B的补集。
sets:
I/x1..x4/;
B(I)/x2/;
C(I)|#not#@in(B,&1):;
Endsets
2,@index([set_name,] primitive_set_element)
返回在集 set_name中原始集成员 primitive_set_element的索引。
如果 set_name被忽略,则将返回与 primitive_set_element匹配的第一个原始集成员的索引。如果找不到,则产生一个错误。
例 4.8如何确定集成员 (B,Y)属于派生集 S3。
sets:
S1/A B C/;
S2/X Y Z/;
S3(S1,S2)/A X,A Z,B Y,C X/;
endsets
X=@in(S3,@index(S1,B),@index(S2,Y));
4.6 集处理函数举例说明有时为 @index指定集的必要性。
例 4.9
sets:
girls/debble,sue,alice/;
boys/bob,joe,sue,fred/;
endsets
I1=@index(sue);
I2=@index(boys,sue);
I1=2,I2=3。建议在使用 @index函数时最好指定集。
3,@wrap(index,limit)
返回 j=index-k*limit,k是整数,取适当值保证 j落在 [1,limit]
内,相当于 index模 limit再加 1。
该函数在循环、多阶段计划编制中特别有用。
4,@size(set_name)
返回集 set_name的成员个数。
模型中明确给出集大小时最好使用该函数。优点是使模型更加数据中立,集大小改变时也更易维护。
遍历整个集进行操作。
语法格式:
@function(setname[(set_index_list)[|conditional_qualifier]]:
expression_list);
@function相应于下面罗列的四个集循环函数之一;
Setname,遍历的集;
set_ index_list,集索引列表;
conditional_qualifier,限制集循环函数范围,当集循环函数遍历集的每个成员时,LINGO都要对 conditional_qualifier进行评价,若结果为真,则对该成员执行 @function操作,否则跳过,继续执行下一次循环。
expression_list,被应用到每个集成员的表达式列表,
当用的是 @for函数时,expression_list可包含多个表达式,其间用分号隔开。这些表达式将被作为约束加到模型中。
当使用其余三个集循环函数时,expression_list只能有一个表达式。如果省略 set_index_list,那么在 expression_list中引用的所有属性的类型都是 setname集。
4.7 集循环函数
1,@for
产生对集成员的约束。
基于建模语言的标量需要显式输入每个约束,但 @for函数允许只输入一个约束,然后自动产生每个集成员的约束。
例 4.10 产生序列 {1,4,9,16,25}
model:
sets:
number/1..5/:x;
endsets
@for(number(I),x(I)=I^2);
end
2,@sum
返回遍历指定的集成员的一个表达式的和。
例 4.11 求向量 [5,1,3,4,6,10]前 5个数的和。
model:
data:
N=6;
enddata
sets:
number/1..N/:x;
endsets
data:
x = 5 1 3 4 6 10;
enddata
s=@sum(number(I) | I #le# 5,x);
end
3,@min和 @max
返回指定的集成员的一个表达式的最小值或最大值。
例 4.12 求向量 [5,1,3,4,6,10]前 5个数的最小值,后 3个数的最大值。
model:
data:
N=6;
enddata
sets:
number/1..N/:x;
endsets
data:
x = 5 1 3 4 6 10;
enddata
minv=@min(number(I) | I #le# 5,x);
maxv=@max(number(I) | I #ge# N-2,x);
end
复杂一点儿的例子。
例 4.13 职员时序安排模型 一项工作一周 7天都需要有人(如护士工作),每天
(周一至周日)所需的最少职员数为 20,16,13,16,19,14,12,并要求每个职员一周连续工作 5天,试求每周所需最少职员数,并给出安排。
注意这里考虑稳定后的情况。
model:
sets:
days/mon..sun/,required,start;
endsets
data:
!每天所需的最少职员数 ;
required = 20 16 13 16 19 14 12;
enddata
!最小化每周所需职员数 ;
min=@sum(days,start);
@for(days(J):
@sum(days(I) | I #le# 5:
start(@wrap(J+I+2,7))) >= required(J));
end
解决方案,每周最少需要 22个职员,周一安排 8人,周二安排 2人,周三无需安排人,周四安排 6人,周五、周六都安排 3人,周日无需安排人。
1,@file函数从外部文件中输入数据,可放在模型中任何地方。
语法格式,@file(’filename’)。
Filename:文件名,可采用相对路径和绝对路径两种表示方式。
对同一文件的两种表示方式的处理和对两个不同的文件处理一样。
例 4.14 以例 1.2为例说明,例 1.2的编码中有两处涉及到数据。
1)集部分 6个 warehouses集成员,8个 vendors集成员;
2)数据部分的 capacity,demand,cost数据。
为使数据和模型完全分开,把它们移到外部的文本文件中。修改模型代码以便于用 @file函数把数据从文本文件中拖到模型中来。
4.8 输入和输出函数将模型和外部数据(如文本文件、数据库、电子表格等)连接起来修改后(修改处代码黑体加粗)的模型代码如下:
model:
!6发点 8收点运输问题 ;
sets:
warehouses/ @file('1_2.ldt') /,capacity;
vendors/ @file('1_2.ldt') /,demand;
links(warehouses,vendors),cost,volume;
endsets
!目标函数 ;
min=@sum(links,cost*volume);
!需求约束 ;
@for(vendors(J):
@sum(warehouses(I),volume(I,J))=demand(J));
!产量约束 ;
@for(warehouses(I):
@sum(vendors(J),volume(I,J))<=capacity(I));
!这里是数据 ;
data:
capacity = @file('1_2.ldt') ;
demand = @file('1_2.ldt') ;
cost = @file('1_2.ldt');
enddata
end
记录,记录结束标记( ~)之间的数据文件部分。
说明,若数据文件中没有记录结束标记,则整个文件被看作单个记录。除了记录结束标记外,模型的文本和数据同它们直接放在模型里一样。
记录结束标记与 @file函数调用的工作方式:
模型第一次调用 @file函数时,打开数据文件,读取第一个记录;
第二次调用 @file函数时,读取第二个记录等等。
说明:
最后一条记录可以没有记录结束标记,遇到文件结束标记时,
LINGO读取最后一条记录,然后关闭文件。若最后一条记录也有记录结束标记,那么直到 LINGO求解完当前模型后才关闭该文件。若打开多个文件,可能导致一些问题,因为这会使同时打开的文件总数超过允许同时打开文件的上限 16。
使用 @file函数时,可将记录内容(除一些记录结束标记外)看作是替代模型中 @file(’filename’)位置的文本。即,一条记录可以是声明的一部分,整个声明,或一系列声明。
数据文件中注释被忽略。注意不允许嵌套调用 @file函数。
2,@text函数将数据部分解输出到文本文件中,可以输出集成员和集属性值语法格式,@text([’filename’])
Filename:文件名,可采用相对路径和绝对路径两种表示方式。
说明,若忽略 filename,则数据输出到标准输出设备(一般是屏幕
)。
@text函数仅能出现在模型数据部分的一条语句左边,右边是集名
(输出该集的所有成员名)或集属性名(输出该集属性的值)。
输出操作,用接口函数产生输出的数据声明。
输出操作仅当求解器求解完模型后才执行,执行次序取决于其在模型中出现的先后。
例 4.15 借用例 4.12,说明 @text用法。
model:
sets:
days/mon..sun/,required,start;
endsets
data:
!每天所需的最少职员数 ;
required = 20 16 13 16 19 14 12;
@text(‘d:\out.txt’)=days '至少需要的职员数,' start;
enddata
!最小化每周所需职员数 ;
min=@sum(days,start);
@for(days(J):
@sum(days(I) | I #le# 5:
start(@wrap(J+I+2,7))) >= required(J));
end
3,@ole函数从 EXCEL中引入或输出数据的接口函数,基于传输的 OLE技术。
说明,OLE传输直接在内存中传输数据,并不借助于中间文件。当使用 @OLE时,LINGO先装载 EXCEL,再通知 EXCEL装载指定的电子数据表,最后从电子数据表中获得 Ranges。为使用 OLE函数,必须有
EXCEL5及其以上版本。
OLE函数可在数据部分和初始部分引入数据。
@OLE可同时读集成员和集属性,集成员最好用文本格式,集属性最好用数值格式。原始集每个集成员需要一个单元 (cell),对于 n元的派生集每个集成员需要 n个单元,第一行的 n个单元对应派生集的第一个集成员,第二行的 n个单元对应派生集的第二个集成员,依此类推。
@OLE只能读一维或二维 Ranges(在单个的 EXCEL工作表 (sheet)中)
,但不能读间断的或三维 Ranges。 Ranges自左而右、自上而下来读例 4.16
sets:
PRODUCT; !产品 ;
MACHINE; !机器 ;
WEEK; !周 ;
ALLOWED(PRODUCT,MACHINE,WEEK):x,y;
!允许组合及属性 ;
endsets
data:
rate=0.01;
PRODUCT,MACHINE,WEEK,ALLOWED,x,y=@OLE('D:\IMPORT
.XLS');
@OLE('D:\IMPORT.XLS')=rate;
enddata
说明,代替在代码文本的数据部分显式输入形式,把相关数据全部放在如下电子数据表中来输入。
下面是 D:\IMPORT.XLS的图表。
除输入数据外,也必须定义 Ranges名,PRODUCT,MACHINE,
WEEK,ALLOWED,x,y。为明确,需要定义如下 Ranges名称:
Name Range
PRODUCT B3:B4
MACHINE C3:C4
WEEK D3:D5
ALLOWED B8:D10
X F8:F10
Y G8:G10
rate C13
定义 Ranges名称步骤:
① 按鼠标左键拖曳选择 Range,
② 释放鼠标按钮,
③ 选择,插入 |名称 |定义,,
④ 输入希望的名字,
⑤ 点击,确定,按钮。
4,@ranged(variable_or_row_name)
为保持最优基不变,变量费用系数或约束行的右端项允许减少的量
5,@rangeu(variable_or_row_name)
为保持最优基不变,变量费用系数或约束行的右端项允许增加的量
6,@status()
返回求解模型结束后的状态:
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时,那么解将不可信,几乎不能用。该函数仅被用在模型的数据部分来输出数据。
例 4.17
model:
min=@sin(x);
data:
@text()=@status();
enddata
end
结果中的 6就是 @status()返回的结果,表明最终解是局部最优的。
7,@dual(variable_or_row_name):
返回变量的判别数(检验数)或约束行的对偶(影子)价格( dual
prices)。
4.9 辅助函数代码:
model:
min=fx+fy;
fx=@if(x #gt# 0,100,0)+2*x;
fy=@if(y #gt# 0,60,0)+3*y;
x+y>=30;
end
1,@if(logical_condition,true_result,false_result)
评价一个逻辑表达式 logical_condition,
若为真,返回 true_ result,否则返回 false_result。



0
20
0
20
1 0 0 2
6 0 3
30
0
,
,
,
,
m i n
..
,
x
xx
y
yy
f x g y
st
x
fx
y
gy
xy
xy





例 4.18 求解最优化问题
2,@warn(’text’,logical_condition)
若条件 logical_condition为真,则产生内容为 ’ text’的信息框。
例 4.19 示例。
model:
x=1;
@warn('x是正数 ',x #gt# 0);
end