本文为作者原创,若需转载使用请注明来源https://www.cnblogs.com/sangxuuan/p/12246622.html
1、这篇帖子适合你么?(必读)
这学期的一门课上接触了LINGO的使用,得益于老师超前的教学理念,我们用不多的课堂时间掌握了第一门像样的求解器语言。对于我们专业来说,第一次使用LINGO是在运筹学课上,作为上机实验求解线性规划模型、目标规划、整数规划等简单的经典模型,一开始认为只要把一些模型简单的输入求解器按下Undo就可以,后来发现求解一些复杂的模型时,简单的誊抄是远远不够的,复杂的逻辑关系表示需要自己进行开发。
这个记录的帖子,是回顾了这一学期我学习LINGO的轨迹,直接从对于数学模型的理解入手,基于对模型的充分理解进行LINGO的使用。
如果你是本科生,这一系列的帖子足够对LINGO进行初步的了解,想要熟练掌握还需要自己进行练习和总结。以下是我的心得和我认为在学习过程中最重要的理念:
最重要的理解数学模型!最重要的理解数学模型!最重要的理解数学模型!
如果在阅读或者编译的过程中出现问题,欢迎和我交流!评论区也好,邮箱也行!
2、先看一个TSP问题吧!
假设一艘船必须访问n个港口(PORT),这n个港口是一个完全图,船需要恰好访问所有港口一次,并且回到起点。
问题的背景就是这样,对于TSP问题,建模的类型以及求解方法在这里先不谈,刚学习LINGO先从标准的问题模型入手:
在这里,i,j表示港口所在的城市
具体的代码如下(没有对应的语言类型,故没有Highlight显示,以加粗代替):
1 data: 2 n=……; 3 enddata 4 sets: 5 city/1..n/; 6 link1(city,city):x,d; 7 link2(city):u; 8 endsets 9 10 data: 11 d= 12 (对d(i,j)进行赋值,可以复制粘贴表格) 13 ; 14 enddata 15 min=@sum( 16 city(i): 17 @sum( 18 city(j): 19 d(i,j)*x(i,j) 20 ) 21 ); 22 23 @for(city(j): 24 @sum(city(i)|i#NE#j: 25 x(i,j) 26 )=1 27 ); 28 @for(city(i): 29 @sum(city(j)|i#NE#j: 30 x(i,j) 31 )=1 32 ); 33 @for(city(i): 34 @for(city(j): 35 @bin(x(i,j)) !@gin整型;@free大于等于零的实数; 36 ) 37 ); 38 39 40 @for(city(i): 41 @for( 42 43 city(j)|(i#GT#1)#AND#(i#LE#n)#AND#(i#NE#j)#AND#(i#NE#1)#AND#(j#NE#1): 44 u(i)+u(j)-x(i,j)*n<=4 45 ) 46 47 );
可以看出,LINGO中可以用函数来进行求和的操作,并且可以对变量进行约束,解决了有些特殊的运算符号无法通过键盘直接输入的问题(很有用啊,不用挠头了,直接掉头发就是了)。具体的每一块怎么使用,我会在这个系列下的随笔慢慢更新。如果哪位朋友看到了这个帖子,想用代码,最好自己打一下,我怕会有小的错误,导致运行不了(问题应该不大,这些例题我都运行通过了)。当然,复制粘贴也是可以的,谁不爱这种方式呢。
在这篇帖子接下来的部分中,我要给大家分享的是如何定义变量,以及在编程过程中会出现的小问题。
3、如何在LINGO中定义变量?
在LINGO中定义变量和常见的计算机编程语言略有不同。一是你可以直接输入,在你的模型规模特别小,能够手动输入的情况下;二是声明一个集合,给集合取个名字,定义好集合内变量的个数,当你需要进行运算时,调用你之前声明过的集合,从里面“拿”。
1 sets: 2 city/1..n/; 3 link1(city,city):x,d; 4 link2(city):u; 5 endsets
定义变量的框架为黄色高亮部分,在LINGO的编辑页面会变成蓝色,仔细观察,如果没变化,你可能是输入错了
第二行是定义了一个城市的集合,当然City你可以进行替换,只要你能记住你定义的集合是代表什么的就行,如果变量数量增加,见名知意很重要。另说明一下,我在完整的程序代码中声明过n=?,所以这里可以写n,不进行全局变量n的赋值,这里请换成具体的数字。
下面说一下3、4两行我干了啥。我们从数学模型可以看出,x&d都是双下标变量,都有i&j,而且i&j都是代表港口所在地城市,故可以从同一个集合City中调用i&j,不要担心重复取值,我们后面会有约束对i&j进行限制。这里需要强调,一定要理清变量角标(甚至是上标)的意义是什么,这里举个例子,多个角标来自于不同的集合,如何对变量进行声明:
1 sets: 2 harbor/1..3/; 3 country/1..5/; 4 months/1..12/; 5 sangxuan1(harbor,country,months):z; 6 sangxuan2(country,harbor):d; 7 endsets
这个例子中,我们不难看出,角标数量的多少决定了你调用集合的多少,不同的关系要在声明中理顺清楚,不然会报错。这里的 z 显然是有三个下标的,分别代表港口、城市、月份(顺序不要搞错!) ;d有两个下标,一个代表城市,一个代表港口。
以上就是最基本的定义变量的方法。后续如果出现特殊情况,我会另作说明进行补充。
4、常见的小问题
常见的小问题大多具有共性,每种编程语言我们多多少少都会遇见,以下是我在学习过程中总结的一点小经验,大家可以参考(LINGO的报错功能没有那么强,所以在使用的时候要非常仔细才行!)
- 输入法要使用英文输入法
- 注释的符号在这里是“!;”组成的,在LINGO编译器界面会变成亮绿色
- “;”结束一段完整的语义
- 使用括号要对齐,检查括号和最后一句话的分号(不能自动缩进,用空格调整,Tab调整的范围太大了)
- 变量角标的逻辑关系要注意,不能大意
- ……(想不起来了,后面慢慢补充,这一部分我会每一期的后面加上这个部分)