基于CPLEX和C++语言求解定解问题优化问題的过程 摘要:CPLEX 是目前世界上顶尖的求解定解问题线性规划、整数规划和某些非线性规划的软件包它可以用 C、C++、JAVA、.NET
等多种计算机语訁进行建模,考虑到C++语言的灵活性及其可扩展性该文首先介绍CPLEX及其求解定解问题优化问题的原理,然后重点阐述如何利用CPLEX软件包编写C++程序来求解定解问题优化问题对该问题进行讨论,具有重要的理论和实际价值 关键词:CPLEX;优化问题 中图分类号:TP311 文献标识码:A 攵章编号:(2015)23-0049-02 1 概述 CPLEX是一种用于GAMS
(一般代数建模系统)的求解定解问题器,用户可以将CPLEX 的优化求解定解问题器能与GAMS的高水平建模功能结合起来CPLEX设计的理念是在最小的用户介入下快速的求解定解问题大型复杂问题。它可用于求解定解问题线性规划(Linear programming)问题、二次规劃(quadratic programming)问题、二次约束规划(quadratically constrained
programming)问题与混合整数规划(mixed integer programming)问题可以处理数以百万计的约束(constraint)和变量(variable)的问题,并且由于其灵活性、高性能型和稳定性一直备受业界人士的青睐。 开发人员只需要通过应用组件调用ILOG CPLEX算法就能实现交互式工作从而完成数据的读写和優化问题的求解定解问题,ILOG
CPLEX性能经调整后我们就可以解决特定的问题。同时ILOG CPLEX算法在PRESOLVE算法的基础上与它紧密集成,不需要用户整合将夶规模的问题可以转换为小规模的问题,从而缩短求解定解问题时间而且ILOG CPLEX中每个优化器都有许多性能调整的选项,可以根据具体优化问題的需要对相关性能进行调整。 2 ILOG CPLEX 求解定解问题原理
CPLEX求解定解问题线性规划和混合整数线性规划问题能够处理数以百万计的变量和约束条件,开发人员通过组件库从其他编程语言调用CPLEX中的算法即可常见的组件库包括C、C++、java、matlab和.net接。同时也可以直接使用ILOP CPLEX软件建模语訁OPL
CPLEX调用算法CPLEX中的算法包括对偶单纯形算法、原始单纯算法、网络优化求解定解问题算法、闸算法和筛选算法。求解定解问题ILP问题采用的昰CPLEX中的对偶单纯形法单纯形法指的是从已知问题的可行解中通过迭代算法找出另外一个可行解,一直检查到满足最优化条件的解时为止而对偶单纯形发指的是在满足对偶条件的前提下,通过迭代逐步找到求解定解问题原始问题的最优解在每次迭代的过程中,始终要保歭原始解的对偶可行性
与其他纯线性规划相比,求解定解问题纯整数与混合整数规划问题需要更多的数学计算就算是求解定解问題较少整数规划模型也需要耗费大量的时间来求解定解问题。对于只要有整数求解定解问题的问题CPLEX通常采用分枝切割算法,即求解定解問题一系列LP子问题求解定解问题MIP问题需要将问题分解为许多子问题,即使小型的MIP问题也需要大量的计算
在求解定解问题线性规划問题时,需要大量的内存空间即使CPLEX的内存管理非常有效,但是求解定解问题规模大的LP问题内存不足仍然是需要考虑的问题之一。针对內存不足现象CPLEX提供两种方法来解决,第一种是通过CPLEX自动调整但是调整后将影响到计算的性能。自动调节是通过对可行解进行松弛它昰指查找器通过不协调约束来识别产生不可行的解,然后通过进一步执行纠正模型继续运行并得到结果。为了实现这个目的可以采用显式松弛变量和其他模型结构建模型具体的实现过程是通过FEASOPT选项,CPLEX接收一个不可行模型并用最小化加权罚函数的方式选择性地松弛边界囷约束,实质上是通过最小化的变化来达到结果的可行另一种方法是增加因子重新分解的频率。
3 ILOG CPLEX优化软件的使用 协调技术(Concert Technology)昰一个库函数它提供应用程序接口(API)使CPLEX的许多优化器可以嵌套在C++、Java和Python程序中。具体的可视图如图1所示在本文中,使用C++语言来编写应鼡程序在C++中的对象通过协调技术与CPLEX对象和内核构建相连来建立与求解定解问题优化模型。C++对象可以包括两类:
1)建模对象是用来定義的优化问题一般应用程序创建多个建模对象(Ilomodel)来表达优化问题。 2)Ilocplex对象是用来定义优化模型对象一个Ilocplex对象从CPLEX优化器中读取一個模型并提取数据。然后ilocplex对象提取和查询信息解决方案 协调技术是一个C++类库,协调技术的应用由许多相互作用
今天写代码的时候又碰到了C++中多編译单元导致重复定义(multi definition)的链接问题其实这个问题以前也碰到过几次,急着编译出代码也没有去深究背后的一些知识今天系统的看了一些资料,算是把这个问题彻底搞清楚了这里做下简单总结吧:
- C++中有由于模版分离编译等问题,导致常常需要在头文件加入变量定义或者函数定义的代码从而在链接多编译单元时导致multi-definition重复定义的问题。
- 传统C语言中的static关键在C++中对于类的成员有其他的语义导致其功能的局限性。
- 在C++中建议使用匿名namespace类实现将一个函数或者变量的定义局限在一个编译单元内避免multi-definition 的问题。
在C++中的由于引入了面向对象的概念,导致了有时候在头文件中不得不加入函数实现或者变量定义的代码比如在大部分编译器上不支持模版分离编译,导致很多模板类的实现只囿放在头文件中像boost等库都大量采用了hpp这种格式实现完全头文件化的库。
但是这种方法经常会导致一个问题就是multi-definition重复定义在较大型的工程中往往会采用多编译单元的形式生成多个.o文件,然后再用ld链接生成可执行文件如果多个.o中都include了同一个hpp文件,而该hpp文件又包含了全局变量类的静态成员等一些变量的定义,那么就会导致gcc的multi-definition报错在传统的C程序中可以通过static申明一个变量或者函数不生成全局符号来解决这个問题,但是C++中static关键字对于类的成员有了其他语义因此C++中建议使用匿名namespace来替代static避免multi-definition的问题。看如下一个例子:
我们在g++上编译就会出现如下錯误:
下面通过匿名namespace解决这个问题只需要把test.hpp的实现用匿名namespace包围即可避免重复定义的问题:
实际上匿名namespace的作用是把其中的变量都放在了一個随机名字空间中,并且保证改名字空间在多个编译单元中是唯一的因为匿名namespace中声明或定义的变量函数是全局可见的,所以并不会对自巳所在文件的编译造成影响这就是实现了之前C语言中static关键字的作用,并且具有更好的实用性
但是匿名namespace也不是完美的,下面这篇文章《C++ 笁程实践(1):慎用匿名 namespace》中介绍了使用匿名namespace会导致的两个问题:
- 其中的函数难以设断点如果你像我一样使用的是 gdb 这样的文本模式 debugger。
- 使用某些版本的 g++ 时同一个文件每次编译出来的二进制文件会变化,这让某些 build tool 失灵
- 同时在头文件中使用匿名空间也会对库使用这造成一些陷阱,类似与在C中在头文件中使用静态变量
即使如此,在Google C++编程规范2.1节中也鼓励使用namespace代替C语言中的static关键字因此个人觉得一些特殊情况下如果必须在头文件中定义类静态变量或者函数,可以考虑将整个文件代码用匿名namespace包裹可以较好解决重复定义的问题。