数学被首页投稿拒绝的稿件数学模型实验相关软件学习

一小时学会用LINGO求解优化模型

2016-06-19  本文已影响4914人  MrGod

Lingo是学习成本最低的,求解各种规模规划问题的神器。本文用一个例子让你快速上手Lingo。

优化问题

现实生活中,遇到的很多问题可以转换到某个优化问题,并给出某种优化模型。优化问题是指求满足一定约束条件下的决策变量,使得目标函数最大或最小的问题。

小红想买若干个茄子、黄瓜和香蕉,每样最多买5斤,总共至少买10斤。茄子10元,黄瓜20元,香蕉30元。问怎样买最省钱。

这个优化问题的各个元素如下

优化问题的可行解指的是满足约束条件的决策变量,比如每样来4斤。最优解指的是使得目标函数取得最大或最小的可行解。

按照决策变量和约束条件的性质,可以把优化问题分为很多种。比如整数规划、线性规划等等。你只需要知道Lingo都可以解决即可。

Lingo

简介

Lingo由美国芝加哥(Chicago)大学的Linus Schrage教授于1980年前后开发,按照求解的规模,分为演示版、学生版、高级版、工业版等等不同的版本。

本文使用的软件,来自于网络上流传已久的版本, LINGO_11.0

使用这个版本是为了方便学习和演示,若要用于商业,还请购买最新的软件。

开始

直接下载并解压,找到解压后的文件夹中的lingo11.exe即可运行。

软件界面是英文,而且据笔者了解暂时没有汉化。默认打开后就是一个标题为LINGO MODEL - LINGO 1的文本输入页面,这是模型页面。我们所有的工作就从这个页面开始。

第一个模型

在模型页面中,复制下列代码。

由于编码的问题,直接复制之后的所有中文字符会无法识别。但lingo支持中文,所以将?删去,改为该有的中文即可。

!模型1:小红买水果;
title: 买用具;
!目标函数;
min = 10*x1+20*x2+30*x3;
!约束条件;
x1<=5;
x2<=5;
x3<=5;
x1+x2+x3>=10;

依次点击菜单栏LINGO->solve ,弹出了求解状态窗口(solver status}和求解报告窗口(solution report).

回顾一下问题。小红想买若干个茄子、黄瓜和香蕉,每样最多买5斤,总共至少买10斤。茄子10元,黄瓜20元,香蕉30元。问怎样买最省钱。

在求解报告窗口中,我们发现找到了全局最优解,目标函数最少为150,也就是最少花150元。在报告中部,变量和值的部分中,看到茄子和香蕉各买5斤。

也许上面一串代码只想让你砸了电脑,放轻松,Lingo不是一门编程语言,只是将我们日常鼠标的操作转化成了命令而已。

基本元素

语句

Lingo的模型文件由一行行语句组成,Lingo在开始求解模型后,会先分析每行语句,并知道自己该干什么。

每行语句必须以英文分号结尾,事实上所有的符号都必须在英文状态下输入。这是因为lingo一般会忽略空格和换行符,所以在Lingo看来

x1
<
=
5;

x1<=5;没什么区别。

值得一提的是,若语法错误,比如输入了中文的分号,Lingo会提示

Incalid input: A syntax error has occurred

并且指出错误的符号。

Lingo中允许注释,并且注释中可以添加中文,就像第一个实例中的一样。注释语句可以出现在任何地方,以!起始,末尾;结束,Lingo会忽略掉这两个符号间的所有内容。就像这样

!ingnoreAnything;

我们推荐模型的每一行都给出注释,因为也许几天后你自己也不知道当初的想法。

变量

Lingo不区分大小写字母, 忽略掉这一点会导致令人沮丧的行为,就像Lingo的脑子突然短路了一样。

变量不必预先定义,直接在语句中出现即可。决策变量也没有特殊的标记,没有赋予初值的变量都作为可变得决策变量。所以不要给决策变量赋初值

变量名必须用字母开头,其后可以是其他字符、数字或下划线。变量名不能超过32个字符。

就像我们在第一个模型中,语句

min = 10*x1+20*x2+30*x3;

创造了x1、x2和x3三个变量,代表茄子、黄瓜和香蕉的购买量。

运算符

Lingo支持所有的数学运算,以及常见的函数。所有支持的函数以及调用的命令,读者可以去查阅Lingo的帮助文档。当然也可以看文末的参考文献。

直接在模型页面输入这个例子,求解试一试

x1+1=1;
x2*1=1;
x3/2=1;
x4-2=1;
x5^2=1;
(x6+x7)^2=1;

有一类特殊的运算符被用在条件判断中,被称为条件和逻辑运算符。在提到集合以及集合函数后,我们会继续介绍。

集合

基本集合

集合是Lingo非常强大的功能。因为我们一般涉及到的数据不会只有两三钟,而是成百上千的数据,以及他们之间彼此构成的百万计的矩阵。

还是从第一个模型开始。

小红看到水果生意这么好做,决定前去批发水果倒卖。假设她要买编号从1到10共10种水果,我们设x(j)为编号为j的水果的购买量,那么j的取值范围就是1到10,这一百个整数。

于是我们建立了一个拥有10个元素的集合。在Lingo中,用集合域来定义集合。

将下面的代码复制到空白的模型文件中,并求解。

!下面sets:和endsets之间的部分被称为集合域;
sets:
!fruit表示一个索引范围,从1到10。x为水果购买量;
fruit/1..10/:x;
!集合域的结束;
endsets

集合使用前,必须在集合域定义。集合域可以出现在模型文件的任意部分,但不能嵌套在其他域中。

一个集合的定义语句包括三个部分:指标集名称、指标范围、集合名称。

fruit/1..10/:x;

其中fruit 为指标集名称,/1..10/ 表示指标范围,x表示拥有指标fruit 的集合。

在一个集合定义语句中,可以一次定义多个拥有相同指标的集合,之间用, 隔开。也可以把集合名留空,表示给一个指标集名称指定了一个指标范围。

fruit/1..100/:x,y;!定义了两个有10个元素的集合;

要想引用集合中的变量,使用

x(1);

这种集合名后附一个括号表示元素的坐标。

派生集合

我们更常用的是派生集合,即有双下标的集合,就是我们日常所说的矩阵。

小红可以从80个供应商手中拿货,将供应商按1到8编号。第i号供应商可以提供的第j号水果的量设为c(ij)。则c(ij)构成的集合共有80个元素,并用两个指标i,j索引。

在Lingo中,使用派生集合来定义这种两个指标集生成的矩阵。

sets:
fruit/1..10/:x;
supplier/1..8/;
supply(supplier,fruit):c;
endsets

上述语句中,先定义了两个指标集fruitsupplier,范围分别是1到10和1到8. 随后用新的语法定义了前面提到的供应量c(ij)

supply(supplier,fruit):c;

这行语句表示定义行坐标为supplier,列坐标为fruit 的新指标集supply ,并且说明变量c 拥有指标集supply. 换句话说,集合c 是一个行坐标为supplier,列坐标为fruit 的矩阵。

引用派生集合中元素的语法为

c(1,1)

这句引用了第一行第一列的元素。

也可以从三个或多个指标集中派生集合。

数据域

定义集合之后,若集合中的元素本身不是决策变量,我们需要对集合中的元素进行赋值。

小红需要知道10种水果的详细价钱,以及8个供应商提供的80个供应量数据。Lingo同样也要知道这些。

sets:
!增加了price集合;
fruit/1..10/:x,price;
supplier/1..8/;
supply(fruit,supplier):c;
endsets

data:
price=10,20,30,40,50,60,20,10,5,10;
c=
1 2 1 3 1 3 1 4 1 4 
1 2 1 3 1 4 1 3 1 4
1 2 1 3 1 3 1 4 1 4
1 2 1 3 1 3 1 4 1 4
1 2 1 3 1 3 1 4 1 4
1 2 1 3 1 3 1 4 1 4
1 2 1 3 1 3 1 4 1 4;
enddata

对非决策变量进行赋值时,赋值语句必须位于数据域中。数据域以data: 开始,enddata 结束。

赋值时,只需要按照数学中的写法将元素排列上去即可。一般派生集合我们采用示例代码中的写法,让人一目了然。但是要注意,c中的换行并不是必须的,只是为了方便人去理解。

集合函数

定义集合的一大优势在于,可以用非常方便的方法遍历集合。

总共有四个集合函数,包括遍历函数@for,求和函数@sum , 最小值函数@min和最大值函数@max

你可以像这样使用集合函数

z=@sum(fruit(j)|j #GE#2 
    :price(j));

上面的语句表示对标号大于等于2的水果的价钱求和。注意其中的换行并不是必须的。

一般语法为

@function(setname(set_index)|conditional_qualifier:

​ expression);

其中function 为四种集合函数,setname 为指标集名称,set_index为指标变量,|conditional_qualifier是条件判断,用于界定指标变量的范围, expression 为所要求的表达式。

逻辑运算符

逻辑运算符只能用于条件判断中,使用条件判断的场合包括上文提到的集合函数,以及@if 语句中。

常用的逻辑运算符有

运算符 意义
#eq# 相等
#ne# 不相等
#gt# 大于
#ge# 大于等于
#lt# 小于
#le# 小于等于
#and#
#or#
#not#

@if是条件函数,一般用在分段函数中。语法为

@if(条件表达式,真值,假值);

如果条件表达式的结果为真,则这个函数返回真值,表达式结果为假,则返回假值。

变量限制

我们在实际应用中,往往要对决策变量做某些限制。比如小红买水果,若商家只愿意整斤卖,则水果购买量必须为整数。

Lingo默认的变量取值范围是大于零的实数,若要指出某个决策变量的限制,需要使用变量限制语句。

变量限制的语法如下

@bin(x);

这行语句表示x只能取0或1.

所有的变量限制语句如下

语句 意义
@bnd(下限,x,上限) x取下限到上限之间的值
@free(x) x取所有实数
@gin(x) x取整数
@bin(x) x取0,1

变量限制语句一般用在@for中。

最终的模型

小红想从8个供货商购进10种水果。从第i个供货商购买第j种水果的购买量为x(ij), 第i个供货商对第j种水果的供应量为c(j), 每种水果统一市场定价为price(j)。若总共进50斤水果,最多从一个购买手中购进10斤水果。水果必须论整斤卖。问怎样购买最省钱。

下面是模型文件的内容。

sets:
!price(j)是每种水果的价钱;
fruit/1..10/:price;
supplier/1..8/;
!c(ij)为供货量,x(ij)为购买量;
supply(supplier,fruit):c,x;
endsets

data:
price=10,20,30,40,50,60,20,10,5,10;
c=
1 2 1 3 1 3 1 4 1 4 
1 2 1 3 1 4 1 3 1 4
1 2 1 3 1 3 1 4 1 4
1 2 1 3 1 3 1 4 1 4
1 2 1 3 1 3 1 4 1 4
1 2 1 3 1 3 1 4 1 4
1 2 1 3 1 3 1 4 1 4
1 2 1 3 1 3 1 4 1 4;
enddata

min = @sum(fruit(J):
        @sum(supplier(I): price(J)*x(I,J)));

!每种水果购买量不超过供应量;
@for(supply(I,J):
    x(I,J) <= c(I,J));

!至少购买50斤水果;
@sum(supply(I,J):
    x(I,J))>=50;

!每种供应商至多购买十斤;
@for(supplier(I):
    @sum(fruit(J):x(I,J))<=10);

!购买量必须为整数;
@for(supply(I,J):
    @gin(x));

Have fun!

上一篇下一篇

猜你喜欢

热点阅读