线性规划之单纯形算法

首先给出转动操作的伪代码(摘自算法导论):

PIVOT

PIVOT\((N,B,A,b,c,v,l,e)\)
\ \ \ \ let \(\hat{A}\) be a new \(m\times n\) matrix
\ \ \ \ \(\hat{b}_{e}=b_l/a_{le}\)
\ \ \ \ for each \(j\in N-\{e\}\)
\ \ \ \ \ \ \ \ \(\hat{a}_{ej}=a_{lj}/a_{le}\)
\ \ \ \ \(\hat{a}_{el}=1/a_{le}\)
\ \ \ \ for each \(i\in B-\{l\}\)
\ \ \ \ \ \ \ \ \(\hat{b}_i=b_i-a_{ie}\hat{b}_e\)
\ \ \ \ \ \ \ \ for each \(j\in N-\{e\}\)
\ \ \ \ \ \ \ \ \ \ \ \ \(\hat{a}_{ij}=a_{ij}-a_{ie}\hat{a}_{ej}\)
\ \ \ \ \ \ \ \ \(\hat{a}_{il}=-a_{ie}\hat{a}_{el}\)
\ \ \ \ \(\hat{v}=v+c_e\hat{b}_e\)
\ \ \ \ for each \(j\in N-\{e\}\)
\ \ \ \ \ \ \ \ \(\hat{c}_j=c_j-c_e\hat{a}_{ej}\)
\ \ \ \ \(\hat{c}_l=-c_e\hat{a}_{el}\)
\ \ \ \ \(\hat{N}=N-\{e\}\cup \{l\}\)
\ \ \ \ \(\hat{B}=B-\{l\}\cup \{e\}\)
\ \ \ \ return\((\hat{N},\hat{B},\hat{A},\hat{b},\hat{c},\hat{v})\)
C++代码:

//输入为一个松弛型
void PIVOT(int l, int e, int flag) { //l为替出变量下标,e为替入变量下标
	b[e] = b[l]/A[l][e]; //一系列更换约束系数的操作 
	for(int j = flag; j <= n+m; ++j) A[e][j] = A[l][j]/A[l][e];
	for(int i = flag; i <= n+m; ++i) {
		if(i == e) continue;
		b[i] = b[i]-A[i][e]*b[e];
		for(int j = flag; j <= n+m; ++j) {
			if(j == e) continue;
			A[i][j] = A[i][j]-A[i][e]*A[e][j];
		}
		A[i][e] = 0;
	}
	v = v+c[e]*b[e]; //更改目标函数 
	for(int i = flag; i <= n+m; ++i) {
		if(i == e) continue;
		c[i] = c[i]-c[e]*A[e][i];
	}
	c[e] = 0;
	B[e] = 1, B[l] = 0; //因为N是B的补集,不用记录
}

然后是单纯形主体:

SIMPLEX

SIMPLEX\((A,b,c)\)
\ \ \ \ \((N,B,A,b,c,v)=\)INITIALIZE-SIMPLEX\((A,b,c)\)
\ \ \ \ let \(\Delta\) be a new vector of length \(m\)
\ \ \ \ while som index \(j\in N\) has \(c_j>0\)
\ \ \ \ \ \ \ \ choose an index \(e\in N\) for which \(c_e>0\)
\ \ \ \ \ \ \ \ for each index \(i\in B\)
\ \ \ \ \ \ \ \ \ \ \ \ if \(a_{ie}>0\)
\ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \(\Delta_i=b_i/a_{ie}\)
\ \ \ \ \ \ \ \ else
\ \ \ \ \ \ \ \ \ \ \ \ \(\Delta_i=\infty\)
\ \ \ \ \ \ \ \ choose an index \(l\in B\) that minimizes \(\Delta_{l}\)
\ \ \ \ \ \ \ \ if \(\Delta_{l}=\infty\)
\ \ \ \ \ \ \ \ \ \ \ \ return "unbounded"
\ \ \ \ \ \ \ \ else \((N,B,A,b,c,v)=\)PIVOT\((N,B,A,b,c,v,l,e)\)
\ \ \ \ for \(i=1\) to \(n\)
\ \ \ \ \ \ \ \ if \(i\in B\)
\ \ \ \ \ \ \ \ \ \ \ \ \(\overline{x}_i=b_i\)
\ \ \ \ \ \ \ \ else
\ \ \ \ \ \ \ \ \ \ \ \ \(\overline{x}_i=0\)
\ \ \ \ return \((\overline{x}_1,\overline{x}_2,\dots,\overline{x}_n)\)

int SIMPLEX() { //-1无解,0无界,1有限最优解
	if(!INITIALIZE_SIMPLEX()) return -1;
	while(1) {
		int e = -1, l = -1;
		for(int i = n+m; i >= 1; --i)
			if(!dcmp(c[i], 0) && c[i] > 0) e = i; //Bland规则
		if(e == -1) break;
		double det = INF;
		for(int i = n+m; i >= 1; --i) {
			if(!B[i] || A[i][e] < 1 || dcmp(A[i][e], 0)) continue;
			if(b[i]/A[i][e] < det || dcmp(det, b[i]/A[i][e])) l = i, det = b[i]/A[i][e];
		}
		if(dcmp(det, INF)) return 0; //无界 
		else PIVOT(l, e, 1);
	}
	return 1; //有限的最优解 
}

最后是初始化可行解:

INITIALIZE-SIMPLE

INITIALIZE-SIMPLEX\((A,b,c)\)
\ \ \ \ let \(k\) be the index of the minimum \(b_i\)
\ \ \ \ if \(b_k\geqslant 0\)
\ \ \ \ \ \ \ \ return \((\{1,2,\dots,n\},\{n+1,n+2,\dots,n+m\},A,b,c,0)\)
\ \ \ \ form \(L_{aux}\) by adding \(-x_0\) to the left-hand side of each constraint and setting the objective function to \(-x_0\)
\ \ \ \ let \((N,B,A,b,c,v)\) be the resulting slack form for \(L_{aux}\)
\ \ \ \ \(l=n+k\)
\ \ \ \ \((N,B,A,b,c,v)=\)PIVOT\((N,B,A,b,c,v,l,0)\)
\ \ \ \ iterate the while loop of lines 3-12 of SIMPLEX until an optimal solution to \(L_{aux}\) is found
\ \ \ \ if the optimal solution to \(L_{aux}\) sets \(\overline{x}_0\) to 0
\ \ \ \ \ \ \ \ if \(\overline{x}_0\) is basic
\ \ \ \ \ \ \ \ \ \ \ \ \({\color{Red} {perform\ one\ (degenerate)\ pivot\ to\ make\ it\ nonbasic}}\)
\ \ \ \ \ \ \ \ from the final slack form of \(L_{aux}\),remove \(x_0\) from the constraints and restore the original objective function of \(L\),but replace each basic variable in this objective function by the right-hand side of its associated constraint
\ \ \ \ \ \ \ \ return the modified final slack form
\ \ \ \ else return "infeasible"
P.S.标红色的那句话不懂什么意思啊, 确实是原文,但我看网上的伪代码里都没有这个判断,这种情况真的会出现吗

//传入一个标准型
//要么返回无解,要么传回一个松弛型的基本可行解 
bool INITIALIZE_SIMPLEX() {
	for(int i = n+1; i <= n+m; ++i) B[i] = A[i][i] = 1;
	int k = n+1;
	for(int i = n+1; i <= n+m; ++i) if(b[i] < b[k]) k = i;
	if(b[k] >= 0) { //基本解可行,直接返回 
		v = 0;
		return 1;
	}
	for(int i = n+1; i <= n+m; ++i) A[i][0] = -1;
	static double c0[N+M+5]; //先拷贝A,c并使z=-x0
	memcpy(c0, c, sizeof c);
	memset(c, 0, sizeof c);
	c[0] = -1;
	PIVOT(k, 0, 0); //需要一组使辅助线性规划成立的基本可行解
	while(1) { //寻找辅助线性规划的最优解 
		int e = -1, l = -1;
		for(int i = n+m; i >= 0; --i)
			if(!dcmp(c[i], 0) && c[i] > 0) e = i; //Bland规则
		if(e == -1) break;
		double det = INF;
		for(int i = n+m; i >= 0; --i) {
			if(!B[i] || A[i][e] < 0 || dcmp(A[i][e], 0)) continue;
			if(b[i]/A[i][e] < det || dcmp(det, b[i]/A[i][e])) l = i, det = b[i]/A[i][e];
		}
		PIVOT(l, e, 0);
	}
	if(dcmp(b[0], 0)) { //找到一个辅助线性规划的最优解,也就是原问题的一个基本可行解
		memcpy(c, c0, sizeof c);
		for(int i = 0; i <= n+m; ++i)
			if(B[i] && !dcmp(c[i], 0)) {
				v = v+c[i]*b[i];
				for(int j = 0; j <= n+m; ++j) {
					if(j == i) continue;
					c[j] = c[j]-c[i]*A[i][j];
				}
				c[i] = 0;
			}
		c[0] = 0;
		for(int i = 0; i <= n+m; ++i)
			if(B[i]) A[i][0] = 0;
		return 1;
	}
	else return 0; //原线性规划无解 
}
posted @ 2019-03-13 10:56  dummyummy  阅读(968)  评论(2编辑  收藏  举报