LINGO10新增功能.docx

上传人:b****6 文档编号:8451860 上传时间:2023-01-31 格式:DOCX 页数:22 大小:26.57KB
下载 相关 举报
LINGO10新增功能.docx_第1页
第1页 / 共22页
LINGO10新增功能.docx_第2页
第2页 / 共22页
LINGO10新增功能.docx_第3页
第3页 / 共22页
LINGO10新增功能.docx_第4页
第4页 / 共22页
LINGO10新增功能.docx_第5页
第5页 / 共22页
点击查看更多>>
下载资源
资源描述

LINGO10新增功能.docx

《LINGO10新增功能.docx》由会员分享,可在线阅读,更多相关《LINGO10新增功能.docx(22页珍藏版)》请在冰豆网上搜索。

LINGO10新增功能.docx

LINGO10新增功能

附录LINGO10.0新增功能介绍

A.2程序流程的控制

A.2.1条件分支控制

在计算段(CALC)中,如果只有当某个条件满足时才执行某个或某些语句,则可以使

用@IFC或@IFC/@ELSE语句,其中@ELSE部分是可选的(在下面的语法中用方括号表示)。

其基本的使用语法是:

@IFC(condition:

executablestatements(可执行语句1);

[@ELSE

executablestatements(可执行语句2);]

其中condition是一个逻辑表达式(表示相应的条件),当condition的逻辑值为“真”

(条件成立)时,程序执行语句1;否则程序执行语句2。

calc:

@WRITE('项目间的消耗系数如下:

');

@WRITE(@NEWLINE

(1));

@WRITEFOR(PART(J):

5*'',PART(J));

@FOR(PART(I):

@WRITE(@NEWLINE

(1),PART(I));

@FOR(PART(J):

@IFC(Req(i,j)#GT#0.0:

@write(@FORMAT(Req(i,j),'#5.0f'));

@ELSE

@WRITE('');

);

);

);

@WRITE(@NEWLINE

(2));

endcalc

运行修改后的程序,相应的输出如下(只列出与计算段的输出相关的部分):

项目间的消耗系数如下:

ABCDEFG

A

B5.

C7.

D9.

E11.

F13.

G15.

下面我们作几点说明:

1.请注意上面程序中的函数@WRITE和@WRITEFOR,它们还可以出现在计算段(CALC)随时输出中间结果,并且不需要使用@TEXT函数,输出的结果也是被定向到缺省的输出设备(通常就是标准的报告窗口)。

如果希望改变缺省的输出设备,可以采用@DIVERT函数作为一个简单例子,我们可以编写以下程序,说明在计算段中可以随时输出中间结果。

calc:

a=5;

@write('a=',a,@newline

(1));

b=8;

a=a+b;

@write('a=',a,@newline

(1));

endcalc

以上程序中第3行和第6行的语句是一样的,但输出结果却会不一样:

a=5

a=13

2.这时计算段中的变量所取的值可能既不是初始参数,又不是整个模型最后的结果,但仍然可以输出中间结果。

而且,LINGO10.0中还增加了条件循环(@WHILE语句)等其他复杂的控制语句,它们通常也要用到@IFC语句。

我们将在后面介绍子模型和条件循环控制时再通过例子进行说明。

3.@IFC函数和以前用过的@IF函数的功能是不同的:

@IFC是引导流程

控制语句的函数(按照不同条件选择不同的程序分支进行执行),而@IF是一个算术函数,

按照不同条件返回不同的计算结果或表达式。

A.2.2条件循环控制及相关语句

如果只要当某个条件满足时就反复执行某个或某些语句,直到条件不成立为止,则可以使用@WHILE语句。

其基本的使用语法是:

@WHILE(condition:

executablestatements(可执行语句);

其中condition是一个逻辑表达式(表示相应的条件),当condition的逻辑值为“真”

(条件成立)时,程序就执行相应的语句,直到条件不成立为止。

请注意,条件循坏控制也

只能出现在计算段(CALC)中。

在条件循环控制中,还经常会使用到循坏跳出控制(@BREAK语句)、程序暂停控制

(@PAUSE语句)以及程序终止控制(@STOP语句):

􀁺@BREAK函数不需要任何参数,其功能是立即终止当前循环,继续执行当前循环

外的下一条语句。

这个函数可以用在条件循环语句(@WHILE语句)中,也可以

用在集合循环语句(@FOR语句)中。

􀁺@PAUSE函数暂停程序执行,并弹出一个窗口,等待用户选择继续执行(RESUME)

或者终止程序(INTERRUPT)。

如果希望在弹出的窗口中显示某些文本信息或某

个变量的当前取值,只需要将这些文本信息或变量作为@PAUSE的调用参数即可。

􀁺@STOP函数终止程序的运行,并弹出一个窗口,说明程序已经停止运行。

如果希

望在弹出的窗口中显示某些文本信息或某个变量的当前取值,只需要将这些文本或

变量作为@STOP的调用参数即可。

例如,如果希望从一个递增排列的正整数数列X中找到某个具体的数KEY在数列X中

所在的位置,可以采用二分搜索算法。

具体的程序是(原程序位于LINGO安装目录的

examples目录下,文件名为LOOPBINS.LG4,这里加上了中文注释):

MODEL:

TITLE二分搜索;!

Binary-search;

SETS:

S1:

X;

ENDSETS

DATA:

KEY=16;!

想要找到的数;

X=2781116202232;!

递增排列的正整数数列;

ENDDATA

CALC:

IB=1;!

搜索位置的最小值;

IE=@SIZE(S1);!

搜索位置的最大值(数列中元素的个数);

@WHILE(IB#LE#IE:

LOC=@FLOOR((IB+IE)/2);!

二分法;

@IFC(KEY#EQ#X(LOC):

@BREAK;!

找到结果,结束循环;

@ELSE

@IFC(KEY#LT#X(LOC):

IE=LOC-1;

@ELSE

IB=LOC+1;

);

);

);

@IFC(IB#LE#IE:

@PAUSE('找到位置:

',LOC);!

显示结果;

@ELSE

@STOP('数列中找不到相应的数!

!

!

');!

程序停止运行;

);

ENDCALC

END

注:

这里集合S1没有显式地定义元素,但由于其属性X有8个元素,因此LINGO自

动认为集合S1={1,2,3,4,5,6,7,8}。

本程序运行时,将找到KEY=16位于数列X中的第5个位置,于是通过@PAUSE语句

将这一信息报告给用户;如果取KEY=15,由于数列X中没有15,程序运行时通过@STOP

语句将这一信息报告给用户。

请读者注意,由于@BREAK函数不需要参数,因此程序中的语句直接写成“@BREAK;”。

而函数@PAUSE和@STOP是可以有参数的,所以程序中即使不给出参数,语句也应该写成

“@PAUSE();”和“@STOP();”,即标示参数表的小括号不能省略,否则就会出现语法错误。

这和以前用过的函数@TEXT的用法非常类似。

A.3子模型功能介绍

A.3.1子模型的定义及求解

在LINGO9.0及更早的版本中,在每个LINGO模型窗口中只允许有一个优化模型,可

以称为主模型(MAINMODEL)。

在LINGO10.0中,每个LINGO模型窗口中除了主模型

外,用户还可以定义子模型(SUBMODEL)。

子模型可以在主模型的计算段中被调用,这就

进一步增强了LINGO的编程能力。

子模型必须包含在主模型之内,即必须位于以“MODEL:

”开头、以“END”结束的模

块内。

同一个主模型中,允许定义多个子模型,所以每个子模型本身必须命名,其基本语法

是:

@SUBMODELmymodel:

可执行语句(约束+目标函数);

ENDSUBMODEL

其中mymodel是该子模型的名字,可执行语句一般是一些约束语句,也可能包含目标

函数,但不可以有自身单独的集合段、数据段、初始段和计算段。

也就是说,同一个主模型

内的变量都是全局变量,这些变量对主模型和所有子模型同样有效。

如果已经定义了子模型mymodel,则在计算段中可以用语句“@SOLVE(submodel);”求

解这个子模型。

我们来看一个背包问题(KnapsackProblem)的例子:

王先生想要出门旅行,需要将一

些旅行用品装入一个旅行背包。

旅行背包有一个重量限制,装入的旅行用品总重量不得超过

30千克。

候选的旅行用品有8件,其重量依次为3、4、6、7、9、10、11、12(千克);

王先生认为这8件旅行用品的价值(或重要性)依次为4、6、7、9、11、12、13、15。

么,为了使背包装入的旅行用品的总价值最大,王先生应该选择哪几件旅行用品?

我们用VAL(I)、WGT(I)分别表示第I件物品的价值和重量,CAP表示背包的重量

限制,用Y(I)表示是否装入第I件物品(0-1决策变量,1表示装,0表示不装)。

容易建

立如下优化模型(直接按LINGO的程序格式写出,命名为文件knapsack01.lg4):

MODEL:

SETS:

ITEM:

WGT,VAL,Y;

ENDSETS

DATA:

VAL=467911121315;

WGT=34679101112;

CAP=30;

ENDDATA

MAX=OBJ;

[Objective]OBJ=@SUM(ITEM(j):

VAL(j)*Y(j));!

目标;

[Capacity]@SUM(ITEM(j):

WGT(j)*Y(j))<=CAP;!

重量约束;

@FOR(ITEM(j):

@BIN(Y(j)));!

0/1变量;

END

求解本模型,可得到最优解Y

(2)=Y(4)=Y(5)=Y(6)=1(其他Y(I)为0),最优值OBJ=38。

对于这样一个简单的模型,上面的程序中只有主模型。

作为一种练习,我们也可以将这

个模型定义为子模型,然后在CALC中进行求解,相应的程序为(命名为文件

knapsack02.lg4)):

MODEL:

SETS:

ITEM:

WGT,VAL,Y;

ENDSETS

DATA:

VAL=467911121315;

WGT=34679101112;

CAP=30;

ENDDATA

SUBMODELKNAPSACK:

!

开始定义子模型KNAPSACK;

MAX=OBJ;

[objective]OBJ=@SUM(ITEM(j):

VAL(j)*Y(j));!

目标;

[capacity]@SUM(ITEM(j):

WGT(j)*Y(j))<=CAP;!

重量约束;

@FOR(ITEM(j):

@BIN(Y(j)));!

0/1变量;

ENDSUBMODEL!

完成子模型KNAPSACK的定义;

CALC:

@SOLVE(KNAPSACK);!

求解子模型KNAPSACK;

ENDCALC

END

求解本模型,得到的结果与不用子模型时相同。

A.3.2求背包问题的多个最好解的例子

对于上面的背包问题,最优解并不是唯一的。

如果我们希望找到所有的最优解(最优值

OBJ=38的所有解),有没有办法呢?

更一般地,能否找出前K个最好的解?

这样我们可以

把这K个最好的解全部列出来,供王先生(决策者)选择。

为了得到第2个最好的解,我们需要再次求解子模型KNAPSACK,但必须排除再次找到

刚刚得到的解Y

(2)=Y(4)=Y(5)=Y(6)=1(其他Y(I)为0)。

因此,我们需要在第2次求解子模型

KNAPSACK时,增加一些约束条件(一般称为“割”)。

生成“割”的方法可能有很多种,

这里我们介绍一种针对0-1变量的特殊处理方法。

对于我们刚刚得到的解Y

(2)=Y(4)=Y(5)=Y(6)=1(其他Y(I)为0),显然满足

Y

(1)-Y

(2)+Y(3)-Y(4)-Y(5)-Y(6)+Y(7)+Y(8)=-4;

这个等式左边就是将刚刚得到的解中取1的Y(I)的系数定义为-1,取0的Y(I)的系数定义

为1,然后求代数和;等式右边就是解中取1的Y(I)的个数的相反数。

为了防止再次求解子模型KNAPSACK时这个解再次出现,就是要防止Y

(2),Y(4),Y(5),

Y(6)同时取1的情况出现。

下面的约束就可以保证做到这一点:

Y

(1)-Y

(2)+Y(3)-Y(4)-Y(5)-Y(6)+Y(7)+Y(8)>=-3;

这个约束就是将上面等式中的右端项增加了1,将等号“=”改成了“>=”。

显然,这

个约束排除了Y

(2),Y(4),Y(5),Y(6)同时取1的情况,因为Y

(2),Y(4),Y(5),Y(6)同时

取1(其他Y(I)=0)不能满足这个约束。

其次,由于Y(I)只能取0或1,这个约束除了排除

Y

(2),Y(4),Y(5),Y(6)同时取1的情况外,没有对原可行解空间增加任何新的限制。

可以想象,增加这个约束后,新的最优解一定与Y

(2)=Y(4)=Y(5)=Y(6)=1(其他Y(I)为

0)不同。

这种处理方法具有一般性,可以用于找出背包问题的前K个最好解。

具体的程序如下(以下程序中取K=7,命名为文件knapsack03.lg4)):

SETS:

ITEM:

WGT,VAL,Y;

SOLN:

RHS;!

RHS表示根据每个最优解生成“割”时的右端项;

SXI(SOLN,ITEM):

COF;!

“割”的系数,即1或-1;

ENDSETS

DATA:

K=7;

VAL=467911121315;

WGT=34679101112;

CAP=30;

SOLN=1..K;

ENDDATA

SUBMODELKNAPSACK:

MAX=OBJ;

OBJ=@SUM(ITEM(j):

VAL(j)*Y(j));!

目标;

@SUM(ITEM(j):

WGT(j)*Y(j))<=CAP;!

重量约束;

@FOR(ITEM(j):

@BIN(Y(j)));!

0/1变量;

@FOR(SOLN(k)|k#LT#ksofar:

!

"割去"(排除)已经得到的解;

@SUM(ITEM(j):

COF(k,j)*Y(j))>=RHS(k);

);

ENDSUBMODEL

CALC:

@divert('knapsack.txt');!

结果保存到文件knapsack.txt;

@FOR(SOLN(ks):

!

对ks=1,2,…,K进行循环;

KSOFAR=ks;!

KSOFAR表示当前正在计算的是第几个最优解;

@SOLVE(KNAPSACK);

RHS(ks)=1;

!

以下打印当前(第ks个)最优解Y及对应的最优值OBJ;

@WRITE('',ks,'',@FORMAT(OBJ,'3.0f'),':

');

@writefor(ITEM(j):

'',Y(j));

@write(@newline

(1));

!

以下计算这个解生成的“割”的系数;

@FOR(ITEM(j):

@IFC(Y(j)#GT#.5:

COF(KS,j)=-1;

RHS(ks)=RHS(ks)-1;

@ELSE

COF(KS,j)=1;

);!

分支@IFC/@ELSE结束;

);!

循环@FOR(ITEM(j)结束;

);!

对ks的循环结束;

@divert();!

关闭文件knapsack.txt,恢复正常输出模式;

ENDCALC

注:

计算段中的语句@divert('knapsack.txt')的含义是,将此后的输出定向到文

本文件knapsack.txt(参见本附录A.4.3节)。

运行这个程序以后,文件knapsack.txt中将包括以下输出(其他输出略去):

138:

01011100

238:

11110100

338:

11000011

437:

11000101

537:

01110001

637:

11111000

737:

01101010

可见,前7个最好的解中,最优值为OBJ=38的解一共有3个,而OBJ=37的解至少有

4个(因为我们只计算了前7个最好的解,我们暂时还无法判断OBJ=37的解是否只有4个),

每个解(Y的取值)也显示在结果报告中了。

A.3.3多个子模型的例子

同一个LINGO主模型中,允许定义多个子模型。

例如,如果我们希望分别求解以下4

个优化问题:

(1)在满足约束x2+4y2≤1且x,y非负的条件下,求x-y的最大值;

(2)在满足约束x2+4y2≤1且x,y非负的条件下,求x+y的最小值;

(3)在满足约束x2+4y2≤1且x,y可取任何实数的条件下,求x-y的最大值;

(4)在满足约束x2+4y2≤1且x,y可取任何实数的条件下,求x+y的最小值。

我们可以编写如下LINGO程序:

MODEL:

SUBMODELOBJ1:

MAX=X-Y;

ENDSUBMODEL

SUBMODELOBJ2:

MIN=X+Y;

ENDSUBMODEL

SUBMODELCON1:

x^2+4*y^2<=1;

ENDSUBMODEL

SUBMODELCON2:

@free(x);

@free(y);

ENDSUBMODEL

CALC:

@write('问题1的解:

',@newline

(1));

@solve(OBJ1,CON1);

@write('问题2的解:

',@newline

(1));

@solve(OBJ2,CON1);

@write('问题3的解:

',@newline

(1));

@solve(OBJ1,CON1,CON2);

@write('问题4的解:

',@newline

(1));

@solve(OBJ2,CON1,CON2);

ENDCALC

END

这个程序中定义了4个子模型,其中OBJ1和OBJ2只有目标(没有约束),而CON1和CON2

只有约束(没有目标)。

在计算段,我们将它们进行不同的组合,分别得到针对问题

(1)~(4)

的优化模型进行求解。

但需要注意,每个@solve命令所带的参数表中的子模型是先合并后求解

的,所以用户必须确保每个@solve命令所带的参数表中的子模型合并后是合理的优化模型,例

如最多只能有一个目标函数。

运行这个后,得到的正是我们预想的结果(只列出部分相关的输出结果):

问题1的解:

Objectivevalue:

1.000000

VariableValueReducedCost

X1.0000000.5198798E-08

Y0.4925230E-081.000000

问题2的解:

Objectivevalue:

0.2403504E-09

VariableValueReducedCost

X0.0000001.000000

Y0.0000001.000000

问题3的解:

Objectivevalue:

1.118034

VariableValueReducedCost

X0.89442720.000000

Y-0.22360680.000000

问题4的解:

Objectivevalue:

-1.118034

VariableValueReducedCost

X-0.89442720.000000

Y-0.22360680.000000

这4个问题都是非常简单的问题,最优解和最优值很容易用解析方法计算得到,读者不

妨用解析方法计算和验证一下以上得到的解的正确性。

A.3.4其他相关函数

1.@GEN

这个函数只能在计算段使用,功能与菜单命令LINGO|Generate和LINGO行命令GEN

类似(参见本书118页和132页),即生成完整的模型并以代数形式显示(主要作用是可供

用户检查模型是否有误)。

当不使用任何调用参数时,“@GEN();”语句只对主模型中出现

在当前“@GEN();”语句之前的模型语句进行处理。

例如,如果在上面A..3.1节的文件knapsack01.lg4中增加以下的计算段:

calc:

@gen();

endcalc

则程序运行时报告窗口的显示为:

MODEL:

[_1]MAX=OBJ;

[OBJECTIVE]OBJ-4*Y_1-6*Y_2-7*Y_3-9*Y_4-11*Y_5–12*

Y_6-13*Y_7-15*Y_8=0;

[CAPACITY]3*Y_1+4*Y_2+6*Y_3+7*Y_4+9*Y_5+10*Y_6+11

*Y_7+12*Y_8<=30;

@BIN(Y_1);@BIN(Y_2);@BIN(Y_3);@BIN(Y_4);@BIN(Y_5);

@BIN(Y_6);@BIN(Y_7);@BIN(Y_8);

END

用户可以指定@GEN处理哪个(或多个)子模型,这只需要将子模型名(可以多个)

作为调用@GEN函数的参数即可。

如果为@GEN函数指定多个子模型作为调用参数,则显

示的是这几个子模型合并后的结果。

例如,如果在上面A..3.1节的文件knapsack02.lg4的计算段中增加以下语句:

@gen(KNAPSACK);

则程序运行时报告窗口的显示与上面的显示相同(因为knapsack02.lg4的子模型KNAPSACK

与knapsack01.lg4的主模型本质上是一样的)。

注意:

如果在上面A..3.2节的文件knapsack03.lg4的计算段中不同的位置增加

@gen(KNAPSACK)语句,将得到不同的输出结果。

这是因为在计算段中不同的位置,子模

型KNAPSACK的具体内容是有可能发生变化的(因为“割”在不断增加)。

例如,如果将

@gen(KNAPSACK)语句增加在计算段的最后,得到的输出为(可以看出此时模型中有6个

“割”,即约束[_4]~[_9]):

MODEL:

[_1]MAX=OBJ;

[_2]OBJ-4*Y_1-6*Y_2-7*Y_3-9*Y_4-11*Y_5-12*Y_6-13

*Y_7-15*Y_8=0;

[_3]3*Y_1+4*Y_2+6*Y_3+7*Y_4+9*Y_5+10*Y_6+11*Y_7

+12*Y_8<=30;

[_4]Y_1-Y_2+Y_3-Y_4-Y_5-Y_6+Y_7+Y_8>=-3;

[_5]-Y_1-Y_2-Y_3-Y_4+Y_5-Y_6+Y_7+Y_8>=-4;

[_6]-Y_1-Y_2+Y_3+Y_4+Y_5+Y_6-

展开阅读全文
相关资源
猜你喜欢
相关搜索

当前位置:首页 > 职业教育 > 其它

copyright@ 2008-2022 冰豆网网站版权所有

经营许可证编号:鄂ICP备2022015515号-1