极力推荐观看教程作者视频,不止包含知识点,同时包含随堂练习帮助你巩固知识。我自己记的笔记肯定没有视频教程讲的仔细。链接
稠密集合与稀疏集合
稠密集合与稀疏集合都是派生集合
稠密集合:如果一个派生集合在定义时省略了元素列表,那么意味着它的元素是全部父集元素的所有组合构成,这样的集合称为稠密集合。(上一篇中使用的都是稠密集合)
稀疏集合:如果派生集合的元素只是稠密集合的一部分(子集),那么这样的派生集合就被称为稀疏集合。
稀疏集合的定义方法
直接罗列法
FACTORY/A`..`3/;
SHOP/B1..B4/;
LINK(FACTORY,SHOP)
/A1 B1,A1 B2,
A2 B3,A2 B4,
A3 B1,A3 B2,A3 B3/;
A1 | A2 | A3 | |
---|---|---|---|
B1 | A1B1 | A3B1 | |
B2 | A1B2 | A3B2 | |
B3 | A2B3 | A3B3 | |
B4 | A2B4 |
可以看到罗列出的元素只有7个。这种方法只适合于数据较少的时候,数据量较大的时候罗列出来会很费时间。
元素过滤法
FACTORY/A1..A3/;
SHOP/B1..B4/;
LINK(FACTORY,SHOP)|&1 #LE# &2;
&1 #LE# &2这部分就是一个逻辑表达,要求第一个个元素(&1)在集合当中的序号小于等于第二个元素(&2)在集合当中的序号。
A1 | A2 | A3 | |
---|---|---|---|
B1 | A1B1 | ||
B2 | A1B2 | A2B2 | |
B3 | A1B3 | A2B3 | A3B3 |
B4 | A1B4 | A2B4 | A3B4 |
例子:最短路径求解
A、B…G七个城市,图中的连线表示城市之间有道路相连,连线旁边的数字表示道路的长度Wij
求A到G的最短路线。
这里引入决策变量xij就表示是否经过该条道路。
这里可以发现,因为路线带箭头(单向),所以有一部分路是无法走的(C到A)所以创建的是稀疏集合
自己编写的,和教程里多多少少有些出入。
MODEL:
SETS:
CITY/A..G/;
LINK(CITY,CITY)/A B,A C,B D,B E,B F,C D,C E,C F,D G,E G,F G/:ROAD,X;
ENDSETS
DATA:
ROAD=2 4 3 5 1 2 3 6 4 3 5;
ENDDATA
MIN=@SUM(LINK:ROAD*X);
@FOR(CITY(I)|I #NE# A #AND# I #NE# G:
@SUM(LINK(I,J):X(I,J))
=
@SUM(LINK(K,I):X(K,I)));
@SUM(LINK(I,J)|I #EQ# A:X(I,J))=1;
@SUM(LINK(I,J)|J #EQ# G:X(I,J))=1;
@FOR(LINK:@BIN(X));
END
例子:网络最大流问题
这个网络可以是交通网、物资流、人流等等,具体问题具体分析套用即可。
这里的每个分支上的数字是表示最大流量,而一条通路上真正最大的流量是取决于这条通路上最大流量最小的那一条。
求解一下1-7最大的流量。
引入决策变量fij和fmax。其中fij是达到最大流时,弧(i,j)上的实际流量(一支上的流量),0≤fij≤cij。fmax就是整个路上的最大流量最小的支路值,也等于顶点流入或终点流出的流量。
@SIZE是集合操作函数,集合中有几个元素就是几,1为起点,n为终点。
函数名 | 返回值 |
---|---|
@IN(s:e) | 如果元素e在集合s中,返回1,否则返回0. |
@SIZE(s) | 返回集合s中的成员个数 |
@INDEX(s:ek) | 返回成员ek在集合s中的顺序号,顺序号从1开始,最大值s中的元素个数 |
@WRAP(I,N) | 用来转换集合两端的索引,到达集合最后一个元素后,再从第一个元素开始索引 |
MODEL:
SETS:
Vertex/1..7/;
arc(Vertex,Vertex)
/1 2,1 3,1 4,
2 3,2 5,
3 4,3 5,3 6,
4 6,
5 6,5 7,
6 7/:C,F;
ENDSETS
DATA:
C=
8 10 12
3 6
4 4 7
8
9 8
10;
ENDDATA
N=@SIZE(Vertex);
MAX=FMAX;
@SUM(ARC(I,J)|I #EQ# 1:F(I,J))=FMAX;
@SUM(ARC(I,J)|J #EQ# N:F(I,J))=FMAX;
@FOR(Vertex(I)|I #GT# 1 #AND# I #LT# N:
@SUM(ARC(K,I):F(K,I))
=
@SUM(ARC(I,J):F(I,J)));
@FOR(ARC(I,J):F(I,J)<=C(I,J));
END
复杂模型构成
MODEL:
TITLE 模型标题;(标题段)
SETS:
(集合段)
ENDSETS
DATA:
(数据段)
ENDDATA
INIT:
(初始段)
ENDINIT
CALC:
(计算段)
ENDCALC
约束段(必须)
END
初始段用来在非线性模型中给决策变量赋予一定的初始值,使求解器能够以此为起点更快地找到最优解。在初始段中赋予初值的变量在求解过程中可以被求解器自由地改变。初始段在线性模型中不起任何作用
计算段是在如果数据需要进一步处理才能进行建模时才出现。LINGO会先计算计算段的内容再处理模型。
对于数据段
对于数据段中需要定义的一些数据,如果在设计时不知道具体值,可以先赋值?,即 变量=? 这样在运行程序的时候,程序会弹出对话框等输入值后再运行。
对于向量A,如果进行A=1; 这样的操作,就相当于对向量A所有元素赋值为1。如果进行 A=1, , , ; 这样的操作,就是只对A1赋值1,其他未赋值分量由LINGO在求解过程中自行确定。
例子:消防站选址问题
一个城市有8个区,每个区最多建一个消防站。假定各个区的消防站都建在每个区的中心,各个区之间消防车形势最长时间如表所示。必须保证在任何一个区域发生火警时,能够在10分钟内有消防车赶到。在此基础上,为节省开支,设置的消防站越少越好。问应该在哪几个区设立消防站。
区 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 |
---|---|---|---|---|---|---|---|---|
1 | 7 | 12 | 18 | 20 | 24 | 26 | 25 | 28 |
2 | 14 | 5 | 8 | 15 | 16 | 18 | 18 | 18 |
3 | 19 | 9 | 4 | 14 | 10 | 22 | 16 | 13 |
4 | 14 | 15 | 15 | 10 | 18 | 15 | 14 | 18 |
5 | 20 | 18 | 12 | 20 | 9 | 25 | 14 | 12 |
6 | 18 | 21 | 20 | 16 | 20 | 6 | 10 | 15 |
7 | 22 | 18 | 20 | 15 | 16 | 15 | 5 | 9 |
8 | 30 | 22 | 15 | 20 | 14 | 18 | 8 | 6 |
建与不建,所以还是0-1问题。即MINX。
对于表中数据,我们很难描述最多十分钟到达,因此需要对数据进一步处理。现在以1表示10分钟能到达,0表示不能。
区 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 |
---|---|---|---|---|---|---|---|---|
1 | 1 | 0 | 0 | 0 | 0 | 0 | 0 | 0 |
2 | 0 | 1 | 1 | 0 | 0 | 0 | 0 | 0 |
3 | 0 | 1 | 1 | 0 | 1 | 0 | 0 | 0 |
4 | 0 | 0 | 0 | 1 | 0 | 0 | 0 | 0 |
5 | 0 | 0 | 0 | 0 | 1 | 0 | 0 | 0 |
6 | 0 | 0 | 0 | 0 | 0 | 1 | 1 | 0 |
7 | 0 | 0 | 0 | 0 | 0 | 0 | 1 | 1 |
8 | 0 | 0 | 0 | 0 | 0 | 0 | 1 | 1 |
用LINGO建模
MODEL:
SETS:
AREA/1..8/:X;
E(AREA,AREA):DIST,REACH;
ENDSETS
DATA:
DIST=
7 12 18 20 24 26 25 28
14 5 8 15 16 18 18 18
19 9 4 14 10 22 16 13
14 15 15 10 18 15 14 18
20 18 12 20 9 25 14 12
18 21 20 16 20 6 10 15
22 18 20 15 16 15 5 9
30 22 15 20 14 18 8 6;
ENDDATA
CALC:
@FOR(AREA(I):
@FOR(AREA(J):
REACH(I,J)
=@IF(DIST(I,J) #LE# 10,1,0)
)
);
ENDCALC
MIN=@SUM(AREA:X);
@FOR(AREA(J):@SUM(AREA(I):X(I)*REACH(I,J))>=1);
@FOR(AREA:@BIN(X));
END
用LINGO访问外部数据
访问方式
剪贴板 文本文件 电子表格 数据库
这部分看视频好像有些跳跃,所以我另找了一些关于LINGO访问外部数据的资料,以下是一些总结:
剪贴板访问
大概就是这种情况,已经在文件中写好了表格的格式,直接复制粘贴即可。
文本文件访问
读取
==@file(文件名)==来调用
该语句一般放在数据段,文件名可以包含路径(不包含就,默认当前文件夹)。文件必须是纯文本文件,例如写字板和记事本 。
不同数据段用 ~ 分开;
同一数据段内多个数据之间用逗号或空分开,结束时 不要 用 ; 结束。
这是用前面运输问题进行修改的程序,我把数据编写到data.txt文件内,放在了F盘内。
MODEL:
SETS:
FACTORY/@FILE(F:\data.txt)/:A;
SHOP/@FILE(F:\data.txt)/:B;
LINK(FACTORY,SHOP):C,X;
ENDSETS
DATA:
A=@FILE(F:\data.txt);
B=@FILE(F:\data.txt);
C=@FILE(F:\data.txt);
ENDDATA
MIN=@SUM(LINK(I,J):C(I,J)*X(I,J));
@FOR(FACTORY(I):
@SUM(SHOP(J):X(I,J))=A(I));
@FOR(SHOP(J):
@SUM(FACTORY(I):X(I,J))=B(J));
END
txt文件内是这样的
可以写注释,但是 慎用中文! 有时候会出现莫名其妙的问题。
!factory;
1 2 3~
!shop;
1 2 3 4~
!a;
7 4 9~
!b;
3 6 5 6~
!C;
3 11 3 10
1 9 2 8
7 4 10 5
也可以只调用一部分数据,例如你把LINGO 中的C直接赋值,这样在txt文件中,参数就到b截止
注意最后一个数据后面没有 ~ 符号!
写入
@text(文件名)=变量名;
如果保存的文件不存在会创建新的。
!!!不要把你的结果输出到你的原始数据文件中,会直接覆盖掉原始数据(血的教训)
电子表格(excel)访问
使用 @ole 调用,既可以导入数据,也可以导出数据。文件要打开状态。
导入
只能用在模型的集合定义段、数据段和初始段。
形式 | 说明 |
---|---|
变量名1,变量名2=@OLE(文件名,数据块名1,数据块名2); | 变量名1=数据块名1 变量名2=数据块名2 |
变量名1,变量名2=@OLE(文件名,数据块名1); | 数据块含两列,第一列给变量1,第二列给变量2 |
变量名1,变量名2=@OLE(文件名); | 默认使用同名数据块 |
数据块名:右键定义名称即可。
变量名1,变量名2=@OLE(文件名,数据块名1,数据块名2);
今天简简单单写一个程序。
MODEL:
SETS:
ITEM/1..6/:A,B;
ENDSETS
DATA:
A,B=@OLE('F:\file_1.xlsx','A_DATA','B_DATA');
ENDDATA
END
利用上面的办法已经把对应元素设置上了数据块的名称
没有问题
变量名1,变量名2=@OLE(文件名,数据块名1);
这里还用刚才的文件,不过需要修改名称。
如何修改?
点击名称修改器就可以进行删除。
将两列命名为TOTAL
MODEL:
SETS:
ITEM/1..6/:A,B;
ENDSETS
DATA:
A,B=@OLE('F:\file_1.xlsx','TOTAL');
ENDDATA
END
变量名1,变量名2=@OLE(文件名);
这次把AB的数据块调换,看看效果。
MODEL:
SETS:
ITEM/1..6/:A,B;
ENDSETS
DATA:
A,B=@OLE('F:\file_1.xlsx');
ENDDATA
END
没有问题。
导出
形式 | 说明 |
---|---|
@OLE(‘文件名’,‘数据块名称1’,‘数据块名称2’)=变量名1,变量名2; | 两个变量内容分别写入指定的文件下已经定义了名称的数据块。要求数据块大小不小于数据,如有数据会被覆盖。 |
@OLE(‘文件名’,‘数据块名称’)=变量名1,变量名2; | 两个变量的数据写入统一数据块,先写1,下一列,然后写2。 |
@OLE(‘文件名’)=变量名1,变量名2; | 默认使用与变量同名块的数据块 |
MODEL:
SETS:
ITEM/1..6/:A,B,C,D,E,F;
ENDSETS
DATA:
A=1 2 3 4 5 6;
B=5 4 3 2 1 0;
C=42 61 37 58 96 25;
D=2 5 8 64 2 3;
E=0 0 0 0 0 0;
F=000 001 010 011 100 111;
@OLE('F:\file_1.xlsx','A','B')=A,B;
@OLE('F:\file_1.xlsx','CD')=C,D;
@OLE('F:\file_1.xlsx')=E,F;
ENDDATA
END
另外,如果你有一列数据,但疏浚块有两列,它会把你的一列数据分成两列写入。
数据库(ODBC)访问
读
变量=@ODBC(‘数据源名称’,‘表名’,‘列名’);
写
@ODBC(‘数据源名称’,‘表名’,‘列名’)=变量;
由于没有使用过数据库文件,因此在这里先不写这个的演示了,用到了再补。