参考参考了《Practical Optimization Methods With Mathematic Applications》中的8.4节中介绍的有效集法(Active-Set),有效集法只能优化中等规模的最优化问题,如果是大规模的最优化问题,应该采用其它算法,代码如下:(代码执行环境VS2010)
QP.h
#ifndef _QP_
#define _QP_
#pragma comment (lib,"./bin/blasd.lib")
#pragma comment (lib,"./bin/clapackd.lib")
#pragma comment (lib,"./bin/libf2cd.lib")
#pragma comment (lib,"./bin/tmglibd.lib")
#include <fstream>
#include <assert.h>
#include <vector>
#include <algorithm>
#include <iostream>
#include "Lapack.h"
using namespace std;
/*
Copy right
用拉格朗日乘子发求解等式约束的二次规划问题
求解问题为:min: 0.5*trans(x)*H*x + trans(c)*x; st: A*x = b
参考了《Practical Optimization Methods With Mathematic Applications》中的8.4节中介绍的有效集法(Active-Set)
*/
class QuadEquLag //等式约束问题
{
private:
//
double** rTmp;
public:
double* optimizer(double** H,int& m, int& n, double* c, double** A, double* b);//返回最优解
void calculateQ(double** reverseH, int& row, int& col, double** A,double** Q);
void calculateR(double** reverseH, int& row, int& col, double** A,double** R);
void twoMatrixMultiply(double** a, int& aRow, int& aCol, char* aType, double** b, int& bRow, int& bCol, char* bType,double** out);
void inverseMatrix(double** a, int& aRow, int& aCol); //计算矩阵a的逆矩阵
void testMatrixInverse();
};
class QuadProg : public QuadEquLag //不等式约束问题
{
private:
public:
//double* optimizer(double** H,int& m, int& n, double* c, double** A, double* b, double* x0);
double optimizer(double** H,int& m, int& n, double* c, double** A, double* b, double* x0); //有效集算法只能处理中等规模的数据
int rank(double** a, int& row, int& col, double tol=1e-10); //用SVD分解计算矩阵的秩
void solveLinearEquations(double** a, int& row, int& col, double*);
bool greaterThanZero(double* in, int& n);
bool equalZero(double* in, int& n);
void updateActiveSet(vector<int>& activeSet, double* mu);
double findAfak(vector<int>& activeSet, double* dk, double** A , double* b,double* xk, int& minIndex, int& m, int& n);
void updataXk(double* xk,double& afak, double* dk, int& n);
};
#endif
#include "QP.h"
void QuadEquLag::testMatrixInverse()
{
int N = 500;
double* A = new double[9];
int LDA = 500;
int* IPIV = new int[N];
int LWORK = N;
double* WORK = new double[N];
int INFO ;
ifstream readin;
readin.open("./data/m.txt",ios_base::in);
int k = 0;
if(readin.is_open())
{
for(int i = 0; i < N; i++)
{
for(int j = 0; j < N; j++)
{
double tmp = 0;
readin>>tmp;
A[k++] = tmp;
}
}
}
int m = N; int n = m;
dgetrf_(&m, &n, A, &m, IPIV, &INFO);
dgetri_(&N, A, &LDA, IPIV, WORK, &LWORK, &INFO);
}
QP.CPP
double* QuadEquLag::optimizer(double** H,int& m, int& n, double* c, double** A, double* b)
{
//H为n维对称阵,A为m*n矩阵,c为n维列向量,b为m维列向量
double* res = new double[n];
inverseMatrix(H, n, n);
double** R = new double*[n];
for(int i = 0; i < n; i++)
{
R[i] = new double[m];
}
calculateR(H, m, n, A,R);
rTmp = R;
double** Q = new double*[n];
for(int i = 0; i < n; i++)
{
Q[i] = new double[n];
}
calculateQ(H, m, n, A,Q);
double* Qc = new double[n];
for(int i = 0; i < n; i++)
{
double tmp1 = 0;
double tmp2 = 0;
for(int k1 = 0; k1 < n; k1++)
{
tmp1 = tmp1 + Q[i][k1]*c[k1];
}
for(int k2 = 0; k2 < m; k2++)
{
tmp2 = tmp2 + R[i][k2]*b[k2];
}
res[i] = -tmp1 + tmp2;
}
for(int i = 0; i < n; i++)
{
delete[] R[i];
}
delete[] R;
for(int i = 0; i < n; i++)
{
delete[] Q[i];
}
delete[] Q;
return res;
}
void QuadEquLag::calculateQ(double** reverseH, int& m, int& n, double** A,double** Q)
{
//H为n维对称阵,A为m*n矩阵,c为n维列向量,b为m维列向量
================计算H逆乘A转置=====================
//double** rHtA = new double*[n]; //n*m维
//for(int i = 0; i < n; i++)
//{
// rHtA[i] = new double[m];
//}
//twoMatrixMultiply(reverseH,n,n,"N",A,m,n,"T",rHtA);
=====================================================
===============计算A乘H逆乘A转置=====================
//double** ArH = new double*[m]; //m*n维
//for(int i = 0; i < m; i++)
//{
// ArH[i] = new double[n];
//}
//twoMatrixMultiply(A,m,n,"N",reverseH,m,n,"N",ArH);
//double** ArHtA = new double*[m];
//for(int i = 0; i < m; i++)
//{
// ArHtA[i] = new double[m];
//}
//twoMatrixMultiply(ArH,m,n,"N",A,m,n,"T",ArHtA);
//inverseMatrix(ArHtA, m, m);
释放内存空间
//for(int i = 0; i < m; i++)
//{
// delete[] ArH[i];
//}
//delete[] ArH;
//double** tmp1 = new double*[n];
//for(int i = 0; i < n; i++)
//{
// tmp1[i] = new double[m];
//}
//calculateR(reverseH, m, n, A,tmp1);
//=====================================================
//=====================计算A乘H逆======================
double** AtH = new double*[m];
for(int i = 0; i < m; i++)
{
AtH[i] = new double[n];
}
twoMatrixMultiply(A,m,n,"N", reverseH,n,n,"N",AtH);
//=====================================================
释放内存
//for(int i = 0; i < n; i++)
//{
// delete[] rHtA[i];
//}
//delete[] rHtA;
//for(int i = 0; i < m; i++)
//{
// delete[] ArHtA[i];
//}
//delete[] ArHtA;
double** tmp2 = new double*[n];
for(int i = 0; i < n; i++)
{
tmp2[i] = new double[n];
}
twoMatrixMultiply(rTmp,n,m,"N", AtH,m,n,"N",tmp2);
//释放内存空间
/*for(int i =0; i < n; i++)
{
delete[] tmp1[i];
}
delete[] tmp1;*/
for(int i =0; i < m; i++)
{
delete[] AtH[i];
}
delete[] AtH;
//两个矩阵相减
for(int i = 0; i < n; i++)
{
for(int j = 0; j < n; j++)
{
Q[i][j] = reverseH[i][j] - tmp2[i][j];
}
}
//=================内存清理==================
for(int i =0; i < n; i++)
{
delete[] tmp2[i];
}
delete[] tmp2;
//===========================================
}
void QuadEquLag::calculateR(double** reverseH, int& m, int& n, double** A,double** R)
{
//H为n维对称阵,A为m*n矩阵,c为n维列向量,b为m维列向量
//================计算H逆乘A转置=====================
double** rHtA = new double*[n]; //n*m维
for(int i = 0; i < n; i++)
{
rHtA[i] = new double[m];
}
twoMatrixMultiply(reverseH,n,n,"N",A,m,n,"T",rHtA);
//=====================================================
//===============计算A乘H逆乘A转置=====================
double** ArH = new double*[m]; //m*n维
for(int i = 0; i < m; i++)
{
ArH[i] = new double[n];
}
twoMatrixMultiply(A,m,n,"N",reverseH,n,n,"N",ArH);
double** ArHtA = new double*[m];
for(int i = 0; i < m; i++)
{
ArHtA[i] = new double[m];
}
twoMatrixMultiply(ArH,m,n,"N",A,m,n,"T",ArHtA);
inverseMatrix(ArHtA, m, m);
//释放内存空间
for(int i = 0; i < m; i++)
{
delete[] ArH[i];
}
delete[] ArH;
twoMatrixMultiply(rHtA,n,m,"N",ArHtA,m,m,"N",R);
//释放内存
for(int i = 0; i < n; i++)
{
delete[] rHtA[i];
}
delete[] rHtA;
for(int i = 0; i < m; i++)
{
delete[] ArHtA[i];
}
delete[] ArHtA;
}
void QuadEquLag::twoMatrixMultiply(double** a, int& aRow, int& aCol, char* aType, double** b, int& bRow, int& bCol, char* bType,double** out)
{
if(strcmp(aType,"N") == 0 && strcmp(bType,"N") == 0)//a*b
{
for(int i = 0; i < aRow; i++)
{
for(int j = 0; j < bCol; j++)
{
double tmp = 0;
for(int k = 0; k < aCol; k++)
{
tmp = tmp + a[i][k]*b[k][j];
}
out[i][j] = tmp;
}
}
}
else if(strcmp(aType,"N") == 0 && strcmp(bType,"T") == 0)//a*trans(b)
{
for(int i = 0; i < aRow; i++)
{
for(int j = 0; j < bRow; j++)
{
double tmp = 0.0;
for(int k = 0; k < bCol; k++)
{
tmp = tmp + a[i][k]*b[j][k];
}
out[i][j] = tmp;
}
}
}
else if(strcmp(aType,"T") == 0 && strcmp(bType,"N") == 0)//trans(a)*b
{
}
else if(strcmp(aType,"T") == 0 && strcmp(bType,"T") == 0)//trans(a)*trans(b)
{
}
}
void QuadEquLag::inverseMatrix(double** a, int& aRow, int& aCol)
{
int N = aRow;
double* A = new double[aRow * aCol];
int LDA = aCol;
int* IPIV = new int[N];
int LWORK = N;
double* WORK = new double[N];
int INFO ;
int k = 0;
for(int i = 0; i < N; i++)
{
for(int j = 0; j < N; j++)
{
A[k++] = a[j][i];
}
}
int m = N; int n = m;
dgetrf_(&m, &n, A, &m, IPIV, &INFO);
if(INFO != 0)
{
printf("dgetrf is wrong");
assert(0);
}
INFO = 1;
dgetri_(&N, A, &LDA, IPIV, WORK, &LWORK, &INFO);
if(INFO != 0)
{
printf("dgetri is wrong");
assert(0);
}
k = 0;
for(int i = 0; i < N; i++)
{
for(int j = 0; j < N; j++)
{
a[j][i] = A[k++];
}
}
delete[] A;
}
//double* QuadProg::optimizer(double** H,int& m, int& n, double* c, double** A, double* b, double* x0)
//{
// //H为n维对称阵,A为m*n矩阵,c为n维列向量,b为m维列向量
//
//
//
// //vector<int> J0 ;
// //
// 1)计算有效集J0,构造矩阵Aa0,设k=0
// //for(int i = 0; i < m; i++)
// //{
// //
// // double tmp = 0.0;
// // for(int j = 0; j < n; j++)
// // {
// // tmp = tmp + A[i][j] * x0[j];
// // }
// // if(tmp == b[i])
// // {
// // J0.push_back(i);
// // }
// //}
// //int nn=0;
// //while(true)
// //{
// // //2)计算gk,并判断rank[trans(Aak) gk] 时候等于 rank(trans(Aak))
// // double* gk = new double[n];
// // for(int i = 0; i < n; i++)
// // {
// // double tmp = 0.0;
// // for(int j = 0; j < n; j++)
// // {
// // tmp = tmp + H[i][j] * x0[j];
// // }
// // gk[i] = c[i] + tmp;
// // }
// // double** Aakgk = new double*[n];
// // for(int i = 0; i < n; i++)
// // {
// // Aakgk[i] = new double[J0.size() + 1];
// // }
// //
// // for(unsigned int i = 0; i < J0.size(); i++)
// // {
// // for(int j = 0; j < n; j++)
// // {
// // Aakgk[j][i] = A[J0[i]][j];
// // }
// // }
// //
// // int last = J0.size();
// // for(int j = 0; j < n; j++)
// // {
// // Aakgk[j][last] = gk[j];
// // }
// //
// // int col = J0.size() + 1;
// // int rankAakgk = rank(Aakgk,n,col);
// // col--;
// // int rankAak = rank(Aakgk, n, col);
//
// // if(rankAakgk == rankAak)
// // {
// // //3)如果gk中的值全大于零,则迭代停止,找到最优值,gk相当于罚参数
// // solveLinearEquations(Aakgk, n, col, gk);//gk作为输入参数和输出参数,最终的方程组的解存储在此
// //
// // for(int i = 0; i < n; i++)
// // {
// // delete[] Aakgk[i];
// // }
// // delete[] Aakgk;
// //
// // /*if(greaterThanZero(gk,n) == true)
// // {
// // delete[] gk;
// // break;
// // }*/
// // if(greaterThanZero(gk,n) == false)
// // {
// // updateActiveSet(J0, gk );//更新有效集,将gk中的负值对应的有效集去掉,如果gk中全部为负值,去掉最小的负值所对应的有效集
// // /*delete[] gk;*/
// // }
// // }
// //
// // //4)求解dk
//
// // double** Aak = new double*[n];
// // int mTmp ;
// // double* bTmp = NULL;
// // if(J0.size() == 0)
// // {
// // for(int i = 0; i < n; i++)
// // {
// // Aak[i] = new double[1];
// // Aak[i][0] = 0.0;
// // }
// // mTmp = 1;
// // bTmp = new double[1];
// // for(unsigned int i = 0; i < 1; i++)
// // {
// // bTmp[i] = 0.0;
// // }
// // }
// // else
// // {
// // for(int i = 0; i < n; i++)
// // {
// // Aak[i] = new double[J0.size()];
// // }
// // for(unsigned int i = 0; i < J0.size(); i++)
// // {
// // for(int j = 0; j < n; j++)
// // {
// // Aak[j][i] = A[J0[i]][j];
// // }
// // }
// //
// // mTmp =(unsigned) J0.size();
// // bTmp = new double[J0.size()];
// // for(unsigned int i = 0; i < J0.size(); i++)
// // {
// // bTmp[i] = 0.0;
// // }
// // }
// // cout<<nn++<<endl;
//
// // double* dk = QuadEquLag::optimizer(H, mTmp, n, gk, Aak, bTmp);
// // if(equalZero(dk,n) == true && greaterThanZero(gk,n) == true)
// // {
// // delete[] gk;
// // break;
// // }
// // {
// // delete[] gk;
// // }
// // for(int i = 0; i < n; i++)
// // {
// // delete[] Aak[i];
// // }
// // delete[] Aak;
// // delete[] bTmp;
// // //5)求解afak,并设置x(k+1) = x(k) + afak*dk;
// // double* xk = x0;
// // int minIndex;
// // double afak = findAfak(J0, dk,A, b, xk,minIndex, m,n);
// // updataXk(xk,afak,dk,n);
//
// // //6)
// // if(afak < 1)
// // {
// // J0.push_back(minIndex);
// // }
// // sort(J0.begin(), J0.end());
// //}
//
// return x0;
//}
double QuadProg::optimizer(double** H,int& m, int& n, double* c, double** A, double* b, double* x0)
{
//H为n维对称阵,A为m*n矩阵,c为n维列向量,b为m维列向量
vector<int> J0 ;
bool come5 = true;
//0)计算有效集J0,构造矩阵Aa0,设k=0
for(int i = 0; i < m; i++)
{
double tmp = 0.0;
for(int j = 0; j < n; j++)
{
tmp = tmp + A[i][j] * x0[j];
}
if(tmp == b[i])
{
J0.push_back(i);
}
}
int nn=0;
double* gk = new double[n];
int nm = 0;
while(true)
{
//1)计算gk,并判断rank[trans(Aak) gk] 时候等于 rank(trans(Aak))
cout << nm++ <<endl;
if(come5 == true)
{
come5 = false;
for(int i = 0; i < n; i++)
{
double tmp = 0.0;
for(int j = 0; j < n; j++)
{
tmp = tmp + H[i][j] * x0[j];
}
gk[i] = c[i] + tmp;
}
}
//2)求解线性方程组[Q A'; A 0]*[dk v]' = [-gk 0]
double* gkk = new double[n+J0.size()];
double** QA = new double*[(n+J0.size())];
for(unsigned int i = 0; i < n+J0.size(); i++)
{
QA[i] = new double[n+J0.size()];
}
for(int i = 0; i < n; i++)
{
for(int j = 0; j < n; j++)
{
QA[i][j] = H[i][j];
}
}
for(unsigned int i = 0; i < J0.size(); i++)
{
for(int j = 0; j < n; j++)
{
QA[i+n][j] = A[J0[i]][j];
}
}
for(unsigned int i = 0; i < J0.size(); i++)
{
for(int j = 0; j < n; j++)
{
QA[j][i+n] = A[J0[i]][j];
}
}
for(unsigned int i = 0; i < J0.size(); i++)
{
for(unsigned int j = 0; j < J0.size(); j++)
{
QA[i+n][j+n] = 0.0;
}
}
for(unsigned int i = 0; i < n+J0.size(); i++)
{
if(i < n)
gkk[i] = -gk[i];
else
gkk[i] = 0.0;
}
int row = n + J0.size();
int col = n + J0.size();
double* gkkk = new double[n+J0.size()];
for(unsigned int i = 0; i < n + J0.size(); i++)
{
gkkk[i] = gkk[i];
}
solveLinearEquations(QA, row, col, gkkk);
for(unsigned int i = 0; i < n+J0.size(); i++)
{
delete[] QA[i];
}
delete[] QA;
//3)如果dk=0,转到6)
if(equalZero(gkkk,n) != true)
{
//4)计算步长
int minIndex;
double afak =findAfak(J0, gkkk, A , b, x0, minIndex, m, n);
if(afak < 1)
{
J0.push_back(minIndex);
}
sort(J0.begin(), J0.end());
//5)更新xk
updataXk(x0,afak,gkkk,n);
come5 = true;
delete[] gkk;
delete[] gkkk;
continue;
}
else if(equalZero(gkkk,n) == true)
{
//6)
double* v = new double[J0.size()];
for(unsigned int i = 0; i < J0.size(); i++)
{
v[i] = gkkk[n+i];
}
int num = J0.size();
if(greaterThanZero(v,num) == true)
{
break;
}
else
{
updateActiveSet(J0, v);
}
delete[] v;
delete[] gkkk;
continue;
}
}
delete[] gk;
double obj = 0;
double* o = new double[n];
for(int i = 0; i < n; i++)
{
double tmp = 0.0;
for(int j = 0; j < n; j++)
{
tmp = tmp + x0[j]*H[i][j];
}
o[i] = tmp;
}
double cx = 0.0;
for(int i = 0; i < n; i++)
{
obj = obj + o[i] * x0[i] ;
cx = cx + c[i] * x0[i];
}
delete[] o;
return obj * 0.5 + cx;
;
}
bool QuadProg::equalZero(double* in, int& n)
{
for(int i = 0; i < n; i++)
{
if(in[i] > 0.0000000001)
return false;
}
return true;
}
void QuadProg::updataXk(double* xk,double& afak, double* dk, int& n)
{
for(int i = 0; i < n; i++)
{
xk[i] = xk[i] + afak * dk[i];
}
}
double QuadProg::findAfak(vector<int>& activeSet, double* dk, double** A, double* b, double* xk, int& minIndex, int& m, int& n)
{
double minVal = 0.0;
vector<double> minVals;
vector<int> minIndexs;
for(int i = 0; i < m; i++)
{
vector<int>::iterator iter = find(activeSet.begin(),activeSet.end(),i);
if(iter == activeSet.end()) //i不在有效集中
{
double aixk = 0.0;
double aidk = 0.0;
for(int j = 0; j < n; j++)
{
aidk = aidk + A[i][j] * dk[j];
aixk = aixk + A[i][j] * xk[j];
}
if(aidk > 0.0)
{
double rating = (-aixk + b[i])/(aidk);
minVals.push_back(rating);
minIndexs.push_back(i);
}
}
}
minVal = minVals[0];
minIndex = minIndexs[0];
for(unsigned int i = 1; i< minVals.size(); i++)
{
if(minVals[i] < minVal)
{
minVal = minVals[i];
minIndex = minIndexs[i];
}
}
if(1 < minVal)
return 1.0;
else
return minVal;
}
void QuadProg::updateActiveSet(vector<int>& activeSet, double* mu)
{
int m =(int) activeSet.size();
int minIndex = 0;
double minVal = mu[0];
for(int i = 1; i < m; i++)
{
if(mu[i] < minVal)
{
minVal = mu[i];
minIndex = i;
}
}
if(minVal < 0.0)
{
activeSet.erase(activeSet.begin() + minIndex);
}
}
bool QuadProg::greaterThanZero(double* in, int& n)
{
for(int i = 0; i < n; i++)
{
if(in[i] < 0.0)
return false;
}
return true;
}
int QuadProg::rank(double** a,int& row, int& col, double tol)
{
/*s = svd(A);rank = sum(s > tol);*/
//===========SVD=============
char jobu[]="A";
char jobvt[]="A";
int m = row;
int n = col;
double *b=new double[m*n];
for(int i=0;i<n;i++)
{
for(int j=0;j<m;j++)
{
b[i*m+j]=a[j][i];
}
}
int lda=m;
int minVal = min(m,n);
double *s=new double[minVal];
int ldu=m;
double *u=new double[ldu*m];
int ldvt=n;
double *vt=new double[ldvt*n];
int lwork=max(1,5*min(m,n));
double *work=new double[lwork];
int info;
dgesvd_(jobu, jobvt, &m, &n, b, &lda, s, u, &ldu, vt, &ldvt, work, &lwork,&info);
//===========================
delete[] b;
delete[] u;
delete[] vt;
int rank = 0;
for(int i = 0; i < minVal; i++)
{
if(s[i] > tol)
rank++;
}
delete[] s;
return rank;
}
void QuadProg::solveLinearEquations(double** a, int& row, int& col, double* b)
{
int N = row;
int NRHS = 1;
double* A = new double[(row) * (col)];
int index = 0;
for(int i = 0; i < row; i++)
{
for(int j = 0; j < col; j++)
{
A[index++] = a[j][i];
}
}
int LDA = row;
int* IPIV = new int[N];
double* B = b;
int LDB = max(1, N);
int INFO;
dgesv_(&N, &NRHS, A, &LDA, IPIV, B, &LDB, &INFO );
delete[] A;
delete[] IPIV;
}
测试主程序为:
#include "src\Optimizer\QP.h"
#include <fstream>
using namespace std;
int main(int argv, char** argc)
{
double** H = new double*[400];
double** A = new double*[400];
for(int i = 0; i < 400; i++)
{
A[i] = new double[400];
H[i] = new double[400];
}
ifstream readin;
readin.open("./data/H.txt");
if(readin.is_open())
{
for(int i = 0; i < 400; i++)
{
for(int j = 0; j < 400; j++)
{
double tmp;
readin >> tmp;
H[i][j] = tmp;
}
}
}
readin.close();
double* f = new double[400];
readin.open("./data/f.txt");
if(readin.is_open())
{
for(int i = 0; i < 400; i++)
{
double tmp;
readin >> tmp;
f[i] = tmp;
}
}
readin.close();
for(int i = 0; i < 400; i++)
{
for(int j = 0; j < 400; j++)
{
A[i][j] = 0.0;
}
A[i][i] = 1.0;
}
double* b = new double[400];
readin.open("./data/b.txt");
if(readin.is_open())
{
for(int i = 0; i < 400; i++)
{
double tmp;
readin >> tmp;
b[i] = tmp;
}
}
b[399] = 1000000000;
readin.close();
double* x0 = new double[400];
for(int i = 0; i < 400; i++)
{
x0[i] = 0.5;
}
int m = 400;
int n = 400;
QuadProg* qu = new QuadProg();
double obj = qu->optimizer( H,m,n, f, A, b, x0);
return 1;
}
本程序中用到了Lapack库和测试数据(Lapack是进行矩阵计算的库,Matlab中也采用了它),
可以到此处下载,漏掉的Lapack.h文件见这里:
点击打开链接