350 likes | 568 Views
BCL 库入门. 林森科技 2007.6. 概述. 输入和求解线性规划问题 混合整数规划 二次规划 启发式算法. 第 2 章 输入和求解线性规划问题. 采用 BCL 库建立 LP 模型; 采用索引集合从文件输入数据; BCL 库的输出工具; 将问题导出到一个矩阵文件。. 建模示例.
E N D
BCL库入门 林森科技 2007.6
概述 • 输入和求解线性规划问题 • 混合整数规划 • 二次规划 • 启发式算法
第2章 输入和求解线性规划问题 • 采用BCL库建立LP模型; • 采用索引集合从文件输入数据; • BCL库的输出工具; • 将问题导出到一个矩阵文件。
建模示例 • 一个投资者希望用一定数量的资金进行投资。他对十种不同的股票进行投资,并估计在一年内投资的收益。下表给出了每种股票的国别,风险类别(R:高风险,N:低风险)和期望投资是收益率(ROI)。投资者确定了某些约束条件。为了分摊风险,他希望对每种股票的投资最多占总资金的30%。进一步,他希望资金的一半能够投资在北美的股票和最多三分之一是高风险投资。这些资金应该怎样在各种股票中进行分配才能达到最大化的收益的目的呢?
BCL模型实现 (1)初始化 • 以C++进行编程 • 同样的模型可以使用BCL库的C,Java或者Visual Basic接口实现 • 需要包含xprb_cpp.h 头文件 • 需要包含对应的静态链接库.lib文件 • 初始化问题(这里是XPRBprob p(“FolioLP”) )
一般结构 • 生成决策变量 (newVar ) • 定义目标函数 (setObj ) • 定义约束条件 (newCtr) • 求解问题 (maxim ) • 输出结果(getObjVal ,getSol )
编译和编程执行 • Window dos命令行: cl /MD /I D:\xpressmp\include D:\xpressmp\lib\xprb.lib foliolp.cpp • Window vc6.0: 将xprb_cpp.h, xprb.lib文件执行文件夹下面,并添加到工程 • Linux或者Solaris系统: cc -D_REENTRANT -I${XPRESSDIR}/include -L${XPRESSDIR}/lib foliolp.C -o foliolp -lxprb
数据的文件输入 • 使用字符串索引的文件例如foliocpplp.dat给定 ! Return "treasury" 5 "hardware" 17 "theater" 26 "telecom" 12 "brewery" 8 "highways" 9 "cars" 7 "bank" 6 "software" 31 "electronics" 21
数据读取 • 使用函数XPRBreadlinecb读取数据 • 格式化字符处“T g”表示,读取实数字符后面的文本字符串,两者通过空格分开(或者制表符) • 如果其中包含空格则通过单引号或者双引号扩起。 • 如果数据文件使用其他的分隔符例如“,”号,则格式化字符串需要根据它进行修改(例如“T,g”)。
实现 • #define DATAFILE "foliocpplp.dat“ void readData(void) { double value; int s; FILE *datafile; char name[100]; SHARES=p.newIndexSet("Shares",NSHARES); // 生成‘SHARES’索引集合 // 从文件读取‘RET’数据 datafile=fopen(DATAFILE,"r"); for(s=0;s<NSHARES;s++) { XPRBreadlinecb(XPRB_FGETS, datafile, 200, "T g", name, &value); RET[SHARES+=name]=value; } fclose(datafile); SHARES.print(); // 输出集合内容 }
输出函数 • BCL库模型对象(XPRBprob,XPRBvar,XPRBctr,XPRBsos,和XPRBindexSet)都具有print方法 : frac[2].print(); • 输出结果: frac2: 0.2
错误处理 • 初始化错误 if(XPRB::init() != 0) { cout << "Initialization failed." << endl; return 1; } • 求解状态检查 char *LPSTATUS[] = {"not loaded", "optimal", "infeasible", "worse than cutoff", "unfinished", "unbounded", "cutoff in dual"}; cout << "Problem status: " << LPSTATUS[p.getLPStat()] << endl;
导出矩阵 • 可以选择扩展MPS和扩展LP格式 • 创建一个Folio.mat文件 : p.exportProb(XPRB_MPS, "Folio"); • LP格式的矩阵使用以下命令: p.setSense(XPRB_MAXIM); p.exportProb(XPRB_LP, "Folio");
第2章 混合整数规划 • 定义不同类型的离散变量 • 通过Xpress-Optimizer优化器获取MIP解的状态 • 理解MIP优化的日志信息
扩展问题描述 • 投资者不希望持有少量的股票。因此他提出了以下两个可能的约束条件: • 1. 限制投资组合中不同股票的支数。 • 2. 如果买进一种股票,它所占的资金至少占预算的10%即。
MIP 模型1:购买股票数有限 • 引入二值变量buy • 引入股票数小于MAXNUM约束条件 • 把新的二值变量buy与原变量frac相关联
模型实现 • 通过XPRB_BV类型表达的二值变量(即只能取0和1) for(s=0;s<NSHARES;s++) { frac[s] = p.newVar("frac", XPRB_PL, 0, 0.3); buy[s] = p.newVar("buy", XPRB_BV); }
结果分析 • 问题统计 • 切割生成的日志信息 • 分支定界搜索的日志信息
MIP模型 2:增加最小投资量限制 • 新的约束条件 • 如果买进一支股票,则投资该股票的金额占整个资金的比例至少大于一个最小值
模型实现 • 定义半连续变量 • 通过类型XPRB_SC定义 • 通过方法setLim设置下界 for(s=0;s<NSHARES;s++) { frac[s] = p.newVar("frac", XPRB_SC, 0, 0.3); frac[s].setLim(0.1); }
第3章 二次规划 • 定义二次规划目标函数; • 逐步定义和求解问题;
问题描述 • 投资者可能从不同的角度考虑投资组合选择问题:现在他希望限制投资风险并获得一定的目标收益,而不是最大化估计收益和限制高风险投资的比例。他采用Markowitz方法来得到股票投资的估计收益的方差/协方差矩阵的估计值。(例如,硬件和软件公司价值趋势趋于一致,但是与戏剧产业的成功相反,因为当人们厌烦新的计算机和计算机游戏时人们会更多的选择去剧院。)戏剧产业的收益是容易发生变化的,但是国债收益是一定的。估计收益和方差/协方差矩阵在下表给出:
问题 • 问题1:投资者采用哪种投资策略可以达到在给定最小目标收益约束的情况下使方差最小? • 问题2:如果投资者希望最多投资四种不同的股票,哪种投资策略的方差最小(同样是在给定最小目标收益约束的情况下)?
模型修改 • 新目标函数:以均方差代替总收益。 • 移除与风险相关的约束条件。 • 增加新的约束条件:目标收益。
模型实现 • 使用函数XPRBreadarrlinecb 读取文件 • 格式(“g”)和分隔符(任意个空格或者制表符)表示一项 • 定义目标函数我们使用二次表达式(类型XPRBquadExp ) • 以方法minim 求解(使用空字符串参数表示默认算法)
混合整数二次规划 • 表达在投资组合中至多有给定支股票 • 引入二值变量buy • 引入股票数小于MAXNUM约束条件 • 把新的二值变量buy与原变量frac相关联
实现 • 定义二值变量 XPRBvar buy[NSHARES]; • 定义限制约束条件 for(s=0;s<NSHARES;s++) Num += buy[s]; p.newCtr(Num <= MAXNUM); • 关联决策变量 for(s=0;s<NSHARES;s++) p.newCtr(frac[s] <= buy[s]);
第4章 启发式算法 • 参数设置; • 存储和恢复基可行解; • 改变变量边界。
固定二值变量启发式算法 • 求解LP松弛问题并保存最优解的基可行解。 • 圆整启发式算法:如果投资比例变量接近0则将对应的“购买”变量等于0,反之,如果相应的值是一个较大的值则它等于1。 • 求解MIP问题。 • 如果搜索到整数可行解,保存为最好解。 • 通过将所有变量设置为原有边界恢复原问题,并加载存储的基可行解。 • 求解原MIP问题,以启发式算法的解作为切割值。
实现 • 定义启发式子函数 void solveHeur(); • 通过函数设置参数 //禁止自动切割 XPRSsetintcontrol(p.getXPRSprob(), XPRS_CUTSTRATEGY, 0); // 禁止自动MIP启发式算法 XPRSsetintcontrol(p.getXPRSprob(), XPRS_HEURSTRATEGY, 0); // 关闭预处理机制 XPRSsetintcontrol(p.getXPRSprob(), XPRS_PRESOLVE, 0); // 获取可行性误差 XPRSgetdblcontrol(p.getXPRSprob(), XPRS_FEASTOL, &TOL);
保存当前基可行解 basis=p.saveBasis(); • 启发式设置边界 for(s=0;s<NSHARES;s++) { bsol[s]=buy[s].getSol(); // 获取'frac'的值 if(bsol[s] < TOL) buy[s].setUB(0); else if(bsol[s] > 0.2-TOL) buy[s].setLB(1); }