一、LINGO中的集
LINGO中有两种类型的集:原始集(primitive set)和派生集(derived set)
原始集:由一些最基本的对象组成的集合。
派生集:由已存在的其它集合定义而成。其中这里的其它集合可以是原始集或者派生集。
1、模型中的集部分
集部分是LINGO模型的一个可选部分。在模型使用集之前,必须在集部分事先定义。其中,集部分的定义以关键字“sets:”开始,以“end sets:”结束。
其中,一个模型中可以没有集部分,或者有一个或多个集部分。并且集的定义可以出现在LINGO程序的任意部分,但要保证集或集的属性在模型的约束中被引用之前就定义了它们。
(1)定义原始集
语法格式:
setname[/member_list/][:attribute_list];
注意:“[ ]”中的部分为可选项,下同。
其中,setname是集的名字,一般具有较好的可读性。并且命名需要遵循以下规则:
1)由字母、数字和下划线组成;
2)以字母或下划线开头;
3)总长度不超过32个字符,且不区分大小写。
(注意:以上的命名规则适用于集成员名与集属性名的命名)
member_list是集成员列表。若集成员放在集定义中,则可分为显式罗列与隐式罗列;若集成员不放在集定义中,则可在数据部分定义它们。
1)集定义部分的显式罗列:将成员名称一一列出,成员之间用逗号或空格隔开,且逗号与空格可以混合使用。
eg.定义一个名为students的原始集,其具有John、Jill、Rose与Mike四个成员,属性有sex与age。
sets:
students/John,Jill,Rose Mike/:sex age;
endsets;
2)集定义部分的隐式罗列:不需要将成员名称一一列出,可采用如下语法:
setname/member1…memberN/[:attribute_list];
3)在数据部分定义集成员。
eg.
!集部分;
sets:
students:sex,age;!此处不再定义集成员;
endsets
!数据部分;
data:
students,sex,age= John 1 16
Jill 0 14
Rose 0 17
Mike 1 13;
enddata
注意:开头用感叹号(!),末尾用分号(;),!表示注释,可跨多行。
其中,集成员无论用何种字符标记,它的索引都是从1开始连续计数。在attribute_list可以指定一个或多个集成员的属性,属性之间必须用逗号隔开。
(2)定义派生集
语法格式:
setname(parent_set_list)[/member_list/][:attribute_list];
其中,setname是集的名字。
parent_set_list是已定义的集的列表,多个时必须用逗号隔开。如果没有指定成员列表,那么LINGO会自动创建父集成员的所有组合作为派生集的成员。派生集的父集既可以是原始集,也可以是其它的派生集。
eg.
sets:
product/A B/;
machine/M N/;
week/1…2/;
allowed(product,machine,week):x;
endsets
此处没有指定成员列表,那么LINGO会自动创建父集成员的所有组合作为派生集的成员,具体如下:
编号 成员
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)
以上,成员列表被忽略,派生集成员由父集成员所有的组合构成,这样的派生集称为稠密集。
如果限制派生集的成员,使它成为父集成员所有组合构成的集合的一个子集,这样的派生集成为稀疏集。
同原始集一样,派生集成员的声明也有三种方式:1)集合定义部分的显式罗列;2)在集合定义部分设置成员资格过滤器;3)在数据部分定义。下面分别来看:
1)集合定义部分的显式罗列:此时必须显式罗列出所有要包含在派生集中的成员,并且罗列的每个成员必须属于稠
密集。
使用前面的例子,显式罗列派生集的成员:allowed(product,machine,week)/A M 1,A N 2,B N 1/;
2)在集合定义部分设置成员资格过滤器。
eg.
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) #gt# 0.5 : x;
endsets
data:
sex,age = 1 16
0 14
0 17
0 13;
friend = 0.3 0.5 0.6;
enddata
用竖线(|)来标记一个成员资格过滤器的开始。#eq#是逻辑运算符,用来判断是否“相等”。 &1可看作派生集的第1个原始父集的索引,它取遍该原始父集的所有成员;&2可看作派生集的第2 个原始父集的索引,它取遍该原始父集的所有成员;&3,&4,……,以此类推。注意如果派生集B的父集是另外的派生集A,那么上面所说的原始父集是集A向前回溯到最终的原始集,其顺序保持不变,并且派生集A的过滤器对派生集B仍然有效。因此,派生集的索引个数是最终原始父集的个数,索引的取值是从原始父集到当前派生集所作限制的总和。
3)在数据部分定义。
此时同前面部分。
二、LINGO中的数据
为所定义的集合赋值。LINGO为用户提供了两个可选部分:输入集成员和数据的数据部分(Data Section)和为决策变量设置初始值的初始部分(Init Section)。
1、数据部分。
数据部分以关键字“data:”开始,以关键字“enddata”结束。在这里,可以指定集成员、集的属性。
格式:
object_list = value_list;
对象列(object_list)包含要指定值的属性名、要设置集成员的集名,用逗号或空格隔开。
注意:一个对象列中至多有一个集名,而属性名可以有任意多。如果对象列中有多个属性名,那么它们的类型必须一致。如果对象列中有一个集名,那么对象列中所有的属性的类型就是这个集。
数值列(value_list)包含要分配给对象列中的对象的值,用逗号或空格隔开。
注意:属性值的个数必须等于集成员的个数。
看下面的例子。
eg.
sets:
set1/A,B,C/: X,Y;
endsets
data:
X=1,2,3;
Y=4,5,6;
enddata
或者
sets:
set1/A,B,C/: X,Y;
endsets
data:
X,Y=1 4
2 5
3 6;
enddata
以上程序效果一样。第一种为分别为属性赋值,第二种为复合数据声明(data statement)。需要注意的是:LINGO在为对象指定值时,首先在n个对象的第1个索引处依次分配数值列中的前n个对象,然后在n个对象的第2个索引处依次分配数值列中紧接着的n个对象,……,以此类推。
(1)参数。
在数据部分也可以指定一些标量变量(scalar variables)。当一个标量变量在数据部分确定时,称之为参数。
eg.假设模型中用利率8.5%作为一个参数,就可以像下面一样输入一个利率作为参数。
data:
interest_rate = .085;
enddata
也可以同时指定多个参数,如:
data:
interest_rate,inflation_rate = .085 .03;
enddata
(2) 实时数据处理
在某些情况下,对于模型中的某些数据并不是定值。譬如模型中有一个通货膨胀率的参数,我们想在2%至6%范围内,对不同的值求解模型,来观察模型的结果对通货膨胀的依赖有多么敏感。我们把这种情况称为实时数据处理。此时,只需
在本该放数的地方输入一个问号(?),如:
data:
interest_rate,inflation_rate = .085 ?;
enddata
注意:除了参数之外,也可以实时输入集的属性值,但不允许实时输入集成员名。
(3) 指定属性为一个值
可以在数据声明的右边输入一个值来把所有的成员的该属性指定为一个值,如:
sets:
days /MO,TU,WE,TH,FR,SA,SU/:needs;
endsets
data:
needs = 20;
enddata
LINGO将用20指定days集的所有成员的needs属性为20。
对于多个属性的情形,如下:
sets:
days /MO,TU,WE,TH,FR,SA,SU/:needs,cost;
endsets
data:
needs cost = 20 100;
enddata
(4) 数据部分的未知数值
有时只想为一个集的部分成员的某个属性指定值,而让其余成员的该属性保持未知,以便让LINGO去求出它们的最优值。在数据声明中输入两个相连的逗号表示该位置对应的集成员的属性值未知。两个逗号间可以有空格,如:
sets:
years/1…5/: capacity;
endsets
data:
capacity = ,34,20,;
enddata
此处属性capacity的第2个和第3个值分别为34和20,其余的未知。
2、 模型的初始部分
初始部分是LINGO提供的另一个可选部分。在初始部分中,可以输入初始声明(initialization statement),和数据部分中的数据声明相同。对实际问题的建模时,初始部分并不起到描述模型的作用,在初始部分输入的值仅被LINGO求解器当作初始点来用,并且仅仅对非线性模型有用。和数据部分指定变量的值不同,LINGO求解器可以自由改变初始部分初始化的变量的值。
一个初始部分以“init:”开始,以“endinit”结束。
初始部分的初始声明规则和数据部分的数据声明规则相同。也就是说,我们可以在声明的左边同时初始化多个集属性,可以把集属性初始化为一个值,可以用问号实现实时数据处理,还可以用逗号指定未知数值,如:
init:
X, Y = 0, .1;
endinit
Y=@log(X);
X^2+ Y^2<=1;
注:好的初始点会减少模型的求解时间。
注:以上内容若出现理解或整理错误,请联系作者改正,谢谢!