CPLEX的OPL语言学习

CPLEX的OPL语言学习创建 OPL 建模语言 是为了简化对数学规划问题的解算

大家好,欢迎来到IT知识分享网。

OPL语言学习

参考B站视频:cplex入门到精通

相关下载资源放在公众号【诸葛小猿】,关键字“cplex”下载上,关注即可获得下载链接。

创建OPL建模语言,是为了简化对数学规划问题的解算。 许多数学规划问题都可以使用计算机语言表达,其语法与这些问题在教科书和科学论文中的标准表示法相似。

1.注释

注释的作用:提高代码的可读性。

注释是给人看的,程序执行时会忽略注释。

1.1.单行注释

快捷键: ctrl + /

// 单行注释 独占一行 int a = 4; // 单行注释 在代码的结尾 

1.2.多行注释

/* 这个是0-1背包问题的模型。 使用注意:...... 说明:.... 作者:..... */ 

2.OPL说明

OPL 的基本构建块是整数、浮点数、字符串、标识和关键字。

OPL 中的标识只能包含字母、数字和下划线,并且不能以数字开始。

OPL 中的字母区分大小写。

OPL 模型包括:

  • 变量声明
  • 可选预处理指令
  • 模型/问题定义
  • 可选处理后指令
  • 可选流控制(main 块)

3.基本数据类型

基本数据类型包括:整数、浮点数、字符串、分段线性函数和分步函数。

3.1.整数int

整数是数字序列,可能带有减号前缀。

整数常量 maxint,它表示可用的最大正整数。

OPL 提供范围整数范围: -maxintmaxint

int a; // 定义 未初始化 默认为0 int b = -3; // 定义并初始化 int c = a*a; // 通过表达式初始化整数 c 初始化为a 的平方 int d = maxint; // 最大整数  int e = -maxint;// 最小整数 - execute{ writeln(a," ",b," ",c," ",d," ",e); // 0 -3 0  - } 
3.2.浮点数float

OPL 中的数据类型 float 使用双精度浮点数。

预定义的浮点常数 infinity,用于表示无穷大符号。

浮点数可以用小数符号(3.4-2.5) 或使用科学记数法(3.5e-3-3.4e10)进行描述。

float a; // 只定义 不初始化 默认为0 float b = 3.14; // 定义并初始化 float c = -3.e8; // 科学计数法 float d = infinity; // 正无穷大 float e = -infinity; // 负无穷大 float f = 2.5*a + b; // 通过表达式初始化float execute{ writeln(a," ", b," ",c," ",d," ",e," ",f); // 0 3.14 - Infinity -Infinity 3.14 } 
3.3.字符串string

转义字符\

string a; string b = ""; string c = "cplex"; string d = "sss\"dddd\n"; // 转义字符 \" string e = "id\tname\tage\taddr\n1\twxl\t30\tshanghai\n"; // 转义字符 \t \n execute{ writeln(a," ",b," ",c," ",d,"",e); } 执行结果: cplex sss"dddd id name age addr 1 wxl 30 shanghai 

4.数据结构

使用整数、浮点数、字符串等基本类型构建的更复杂的数据结构。

范围(range)、数组(array)、元组(tuple)和集合(set)

4.1范围range

范围运算符:…。

上界小于下界,范围为空。空范围自动规范化为 0..-1。所有空范围都相等。

4.1.1 整数range

要指定整数范围,可以给出其下界和上界,上界和下界必须都为整数。步长为1。

作用:

1.数组初始化时,作为数组索引;arr[R]

2.用作迭代范围。for(i in R) forall (i in R)

3.用作整数决策变量的定义域。dvar int i in R;

4.1.2.浮点型range

定义:range float X = 1.0…100.0;

作用:通常用作浮点决策变量的定义域: dvar float x in X;

range a; // 定义 不初始化 默认为范围为空 0..-1 range b = 1..5; // 整数范围,下界和上界 range c = -5..6; range d = 9..3; // 空范围 0..-1 //range f = 1.0..5.0;// 语法错误 上下界必须为整数 int n = 8; range e = n+1..2*n+1; // 通过表达式来给出下界和上界 range R = 1..100; int arr[R]; // 数组arr有100个元素,元素的索引为1到100 range float g = -2.1..3.8; // 浮点数类型的range execute{ writeln(a); writeln(b); writeln(c); writeln(d); writeln(e); for(i in b){ writeln(i); } // for (j in g){ // 浮点数类型 不能循环打印 // writeln(j); // } } 执行结果: 0..-1 1..5 -5..6 0..-1 9..17 1 2 3 4 5 
4.2.数组array

分一维数组和多维数组。

符号:[ ]

4.1.一维数组

一维数组在 OPL 中时最简单的数组,根据元素和索引集的类型而有所不同。

OPL 中的数组的索引可以是整数range或其他任意有限集合。

int a[1..4]; // 只定义 不初始化 默认[0,0,0,0] // 使用整形range作为数组的索引 int b[1..4] = [10, 20, 30, 40]; // 整数数组 range r = 1..4; float c[r] = [1.2, 2.3, 3.4, 4.5]; // 将range定义和数组定义分开 浮点数数组 string d[1..2] = ["Monday", "Wednesday"]; // 字符串数组 {string} name = {"zhangsan","lisi","wangwu"}; // 定义一个字符串集合 float salary[name] = [1000.0,2000,8000]; // 以字符串集合为索引 非常有用 execute{ writeln(a); writeln(b); writeln(c); writeln(d); writeln(salary); writeln(salary["zhangsan"]); // writeln(salary["zhaoliu"]); // 查询不存在的索引对应的元素 报错 for(i in r){ writeln(c[i]) // 根据索引访问每一个元素 } } 结果: [1.2 2.3 3.4 4.5] ["Monday" "Wednesday"] [1000 2000 8000] 1000 1.2 2.3 3.4 4.5 
4.2.多维数组

声明:

int a[1..2][1..3] = ...; 

元素表示:

元素形式为 a[index1][index2] 的二维数组 
4.3.元组tuple
4.3.1.元组的定义和使用

元组可以将紧密相关的数据聚集在一起。类似于面向对象的类class。

符号:< >

声明及初始化:

tuple Point { key int id; int x; int y; }; // 定义元组 Point dot = <1,2>; // 初始化一个元组 Point point[i in 1..3] = <i, i+1>; // 初始化多个元组 {Point} set ={<1,1,2>,<2,3,4>} 

一旦声明了元组类型 T,便可以定义元组数组、元组集合、元组的元组。元组中不支持多维数组。

传统方式访问元组的各个字段,可以在元组名称中添加点和字段名称后缀。

元组结构可以与关联。 元组键能够访问元组中使用一组唯一标识来组织的数据。

使用关键字:key

单个键: 只有一个key字段。key是唯一键。

多个键:有多个key字段。多个key字段合起来后是唯一键

在元组声明key的作用:

  • 键字段可以用作元组的唯一标识。元组集合中的元素的key不能重复,否则 OPL 会报错。
  • 定义key后,可以仅使用key字段的值来访问元组集合的元素。 可以仅使用键字段来对元组集合进行分类。
tuple Point { int x; int y; } // 声明一个元组 Point dot = <1,2>; // 初始化一个元组对象 Point point[i in 1..3] = <i, i+1>; // 初始化一个元组数组,包含3个元组对象 {Point} points = {<1,2>, <2,3>}; // 初始化一个元组集合,包含2个元组对象 tuple Rectangle { Point ll; Point ur; }// 定义一个元组的元组 Rectangle rect = <<3,4>,<5,6>>; // 初始化一个元组为元素的元组 tuple KeyPoint1 { key int id; int x; int y; }; // 声明含有单个key的元组 tuple Point { int x; int y; } // 声明一个元组 Point dot = <1,2>; // 初始化一个元组对象 Point point[i in 1..3] = <i, i+1>; // 初始化一个元组数组,包含3个元组对象 {Point} points = {<1,2>, <2,3>}; // 初始化一个元组集合,包含2个元组对象 tuple Rectangle { Point ll; Point ur; }// 定义一个元组的元组 Rectangle rect = <<3,4>,<5,6>>; // 初始化一个元组为元素的元组 tuple KeyPoint1 { key int id; int x; int y; }; // 声明含有单个key的元组 KeyPoint1 tup1 = <11,1,1>; KeyPoint1 tup2 = <22,2,2>; KeyPoint1 tup3 = <33,3,3>; KeyPoint1 tup11 = <11,1,1>; {KeyPoint1} kpSet = {tup1,tup2,tup3}; // 元组集合中的元素的key如果重复则下面arr运行时报错 int arr[kpSet] = [100,200,300]; // 元组作为数组的索引 tuple KeyPoint2 { key int id; key string name; int x; int y; }; // 声明含有两个key的元组 //dvar int x; //dexpr int y[t in kpSet] = 3*x; // 元组索引 //subject to { // forall(<a,b,c> in kpSet){ // 不能元组模式 也要用元组索引 forall(t in kpSet){ // y[<a,b,c>]==4; // } //}; execute{ writeln(dot); writeln(point); writeln(points); writeln(rect); writeln(dot.x); // 访问单个字段 writeln(point[1].x); writeln(rect.ll.x); writeln(kpSet.get(33)) // 只通过key=33获取元组集合的元素 for(n in kpSet){ // n是元组索引 writeln(arr[n]); } writeln(arr[tup1]); // 可以只使用元组的key获取元组数组的值 writeln(arr[tup11]); // 可以只使用元组的key获取元组数组的值 // writeln(arr[<11,1,1>]); // 不能使用arr[<11,1,1>] // writeln(arr[<11>]); // 不能使用arr[<11>] } // 结果: <1 2> [ <1 2> <2 3> <3 4>] {<1 2> <2 3>} <<3 4> <5 6>> 1 1 3 <33 3 3> 100 200 300 100 100 
4.3.2元组中的数据类型限制

1.元组中允许的数据类型

  • 原语(int、float 和 string)
  • 元组(也称为子元组)
  • 具有原语项的数组(非字符串),即:整数或浮动数组
  • 具有原语项的集合,即:整数、浮点数或字符串集

2.元组中不允许的数据类型

  • 元组集
  • 字符串、元组和元组集的数组
  • 多维数组

不能将声明中的元组索引和模式与决策表达式dexpr 混合使用:

dexpr float y[i in t] = ...; // 元组索引 subject to { forall(<a,b,c> in t) y[<a,b,c>]==...; }; // 元组模式 或 dexpr float y[<a,b,c> in t] = ...; // 元组模式 subject to { forall(i in t) y[i]==...; // 元组索引 }; 

如果选择在大型模型中标注约束,请使用元组索引来代替元组模式,从而避免增加性能和内存开销。

4.4.集合set
4.4.1.集合定义和初始化

集合set:没有重复元素(重复元素只保留一份),没有索引,默认已排序,顺序是创建顺序。

支持使用任意类型的集。

符号:{ }

定义:{type} 或 setof(type)

默认情况下,集合是已排序状态(ordered),意味着:

  • 其元素顺序被视为创建顺序。
  • 应用于有序集的函数和运算保留此顺序。
/* 1.集合的定义和初始化 */ {string} nameSet; // 定义方式一: {type} 默认是空集{} setof(string) citySet; // 定义方式二: setof(type) 默认是空集{} {string} nameSet1 = {"zhangsan","lisi","wangwu"}; // 定义并初始化 setof(string) citySet1 = {"shanghai","beijing","shengzhen"}; // 定义并初始化 ordered {string} nameSet2 = {"zhangsan","lisi","wangwu"}; // 定义并初始化 上面默认都是 ordered: 默认集合是已排序状态,元素顺序为创建顺序 ordered setof(string) citySet2 = {"shanghai","beijing","shengzhen"}; {string} nameSet3 = {"zhangsan","wangwu","zhangsan","lisi","wangwu"}; // 定义并初始化 重复元素只保留一次 tuple Point { int before; int after; } {Point} points = {<1,2>, <1,3>, <3,4>}; // 定义并初始化 集合内部可以是任意类型的元素 execute{ writeln(nameSet); writeln(citySet); writeln(nameSet1); writeln(citySet1); writeln(nameSet2); writeln(citySet2); writeln(nameSet3); writeln(points); for(i in points){ writeln(i); // 查看每一个集合的元素 } } 
4.4.2.集合运算

在集合上允许以下运算:union、inter、diff 和 symdiff、first 和 last、next 和 prev、nextc 和 prevc、item、ord。 对于集上的函数,索引从 0 开始。

/* 2.集合的运算 */ {string} nameSet1 = {"zhangsan","lisi","wangwu"}; setof(string) citySet3 = {"beijing","shengzhen"}; setof(string) citySet4 = {"shanghai","beijing"}; {string} unionset = citySet3 union citySet4; // 并集 {"beijing" "shengzhen" "shanghai"} {string} interset = citySet3 inter citySet4; // 交集 {"beijing"} {string} diffset = citySet3 diff citySet4; // 差集:第一个集合独有的元素 {"shengzhen"} {string} symdiffset = citySet3 symdiff citySet4; // 两个系统中都单独存在的元素 {"shengzhen" "shanghai"} string firstStr = first(citySet3); // beijing string lastStr = last(citySet3); // shengzhen string ele = next(nameSet1,"zhangsan",2); // 在集合中找zhangsan后面的第二个 集合中如果有重复元素会报错 string ele1 = item(nameSet1,1); // 找到索引值为1的元素 int ele2 = ord(nameSet1,"zhangsan"); // 找到zhangsan的索引值:0 execute{ writeln(unionset); writeln(interset); writeln(diffset); writeln(symdiffset); writeln(firstStr); writeln(lastStr); writeln(ele); writeln(ele1); writeln(ele2); } 
4.4.3.集合的顺序

集合可以是已排序状态、已排列状态或逆序状态。默认是已排序状态,默认是输入顺序。

可以使用sorted或reversed对集合排序。

元组集合如果有key,则根据每个key排序。如果没有key,则所有字段(有例外)参与排序。

ordered {string} orderedSet = {"shanghai","beijing","shengzhen","123cc","2frsd"}; // 已排序集合 默认情况 输入的顺序 sorted {string} sortedSet = {"shanghai","beijing","shengzhen","123cc","2frsd"}; // 已排列集合 元素按照自然升序(或降序)顺序排列。 字符串是词典序。 reversed {string} reversedSet = {"shanghai","beijing","shengzhen","123cc","2frsd"}; // 逆序排列集合 tuple Point { key string name; int x; int y; } {Point} tupSet1 = {<"bcd",1,2>, <"abd",1,3>, <"abc",3,4>}; reversed {Point} tupSet2 = {<"bcd",1,2>, <"abd",1,3>, <"abc",3,4>}; // 逆序元组集合 比较元组的所有key,先比较第一个key然后比较第二个key。元组集合不使用键,那么会将整个元组(除了固定字段和数组字段)都考虑到排列操作中 sorted {Point} sortedTupSet2 = {<i,j,k> | <i,j,k> in tupSet1}; execute{ writeln(orderedSet); // {"shanghai" "beijing" "shengzhen" "123cc" "2frsd"} writeln(sortedSet); // {"123cc" "2frsd" "beijing" "shanghai" "shengzhen"} writeln(reversedSet);// {"shengzhen" "shanghai" "beijing" "2frsd" "123cc"} writeln(tupSet1); // {<"bcd" 1 2> <"abd" 1 3> <"abc" 3 4>} writeln(tupSet2); // {<"bcd" 1 2> <"abd" 1 3> <"abc" 3 4>} writeln(sortedTupSet2);// {<"abc" 3 4> <"abd" 1 3> <"bcd" 1 2>} } 
4.5.通过数据结构解决稀疏性问题

就矩阵(或二维数组)而言,稀疏性指包含许多零值。

比如有100个仓库,给50个人发货,要算出这50个人的订单,应该最多只有50种仓库到用户的距离不为0,其余全为0;通过以下形式的声明可以利用稀疏性:

{string} Warehouses = ...; {string} Customers = ...; tuple Route { string w; string c; } {Route} routes = ...; int transp[routes] = ... ; 

该声明指定集 routes,该集仅包含相关对 (warehouse, customer)。 然后,数组 transp 可以按此集编制索引,从而利用应用程序中存在的稀疏性。 很明显,对于大规模应用程序,这种方法会大幅降低内存消耗量,计算时间。

可以通过列出其值来初始化数组,就像目前为止提供的大部分示例一样。

免责声明:本站所有文章内容,图片,视频等均是来源于用户投稿和互联网及文摘转载整编而成,不代表本站观点,不承担相关法律责任。其著作权各归其原作者或其出版社所有。如发现本站有涉嫌抄袭侵权/违法违规的内容,侵犯到您的权益,请在线联系站长,一经查实,本站将立刻删除。 本文来自网络,若有侵权,请联系删除,如若转载,请注明出处:https://haidsoft.com/117070.html

(0)
上一篇 2025-11-24 07:26
下一篇 2025-11-24 07:45

相关推荐

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注

关注微信