Live2D

Note -「线性规划」学习笔记

Definition

  线性规划(Linear Programming, LP)形式上是对如下问题的描述:

maximize    z=i=1ncixis.t.{j=1naijxjbii=1,2,,mxi0i=1,2,,n

其中,maximizez 即最大化 z 的值,亦可作 minimumz(最小化),我们称其为目标函数s.t.(subject to,服从)称为约束条件。像这样的问题形式是线性规划的标准型。对于以下问题:

maximize    z=i=1ncixis.t.{j=1naijxj=bii=1,2,,mxi0i=1,2,,n

则是所谓松弛型,因为我们可以通过引入松弛变量将标准型转化为松弛型:

maximize    z=i=1ncixis.t.{xi+n=bij=1naijxji=1,2,,mxi0i=1,2,,m+n

所以数学考试大方程题的第一问也是线性规划。此时,原有的变量 x1,x2,,xn 称为非基变量,松弛用的变量 xn+1,xn+2,,xn+mm 为约束个数)称为基变量。更简便地,我们用矩阵来描述标准形式:

maximize    z=cTxs.t.{Ax=bxi0

其中,

c=(c1c2cn)A=(a11a12a1na21a22a2nam1am2amn)

并称 A约束矩阵b资源向量c价值向量x决策价值变量向量

Algorithm

  说了那么多定义,接下来我们来设计一个算法求解线性规划问题。算法的基本思路为:

  初始找出一个满足所有约束的初始解,通过这个解不断的更新,找到更优的解,直到找不到位置(类似爬山算法)。

  可以发现这样一定能找到最优解,因为如果把求解的 X 当做 n 维空间的一个点,约束条件一定在 n 维空间中框出一个 n 维凸包,一 个解为凸包的一个顶点。凸面体从哪一个发现看都是单峰的,用爬山的算法是肯定能找到最优解的。

  以

maximize    z=3x1+x2+2x3+0x4+0x5+0x6s.t.{x4=30(x1+x2+3x3)x5=24(2x1+2x2+5x3)x6=36(4x1+x2+2x3)x1,x2,,x60

为例,不难看出初始解可为 x=(0,0,0,30,24,36)。接着变换第三个式子:

x6=36(4x1+x2+2x3)x1=914x212x314x6

代入 z,得到:

z=27+34x2+32x334x6

  如此,交换 z 表达式中的一个非基变量和一个基变量的操作称为转轴(pivot)。形式地,设交换非基变量 xu 与基变量 xv+n

xv+n=bvi=1navixi    xu=bvxv+ni=1n[iu]avixiavu

上例中,转轴 x1x6,再将新的等式依次代入其他等式和目标函数,得到了 z=27+34x2+32x334x6。如果我们通过若干次转轴使得 z 表达式中所有变量系数非正且 bi 非负,就能得到此时的常数项为 z 的最值。总结一下,我们的算法分为两步:

  1. 找初始解。一种比较好写的随机算法:随机取 bi<0,再随机取 aij<0,转轴 xjxi+n。不存在 bi 时结束。虽然这样仅保证 bi0更为精准的算法见学长的博客
  2. 转轴使得 cj0。取 j=argmax{cj|cj>0},找出 i=argmin{biaij|aij>0},最后转轴 xjxi+n。如此仍能保持 bi0

  无解当且仅当找不到初始解,这显而易见;无界当且仅当第二步找不到 i,因为此时任意增大 cj>0 的非基变量 xj,只会使 zb 中某些元素增大而不可能减少,一定能够保持合法性。

Example

  UOJ #179 97 分代码:

/* Clearink */

#include <ctime>
#include <cstdio>
#include <random>

#define rep( i, l, r ) for ( int i = l, rpbound##i = r; i <= rpbound##i; ++i )
#define per( i, r, l ) for ( int i = r, rpbound##i = l; i >= rpbound##i; --i )

const int MAXN = 50;
const double EPS = 1e-9;
int n, m, type, id[MAXN + 5], ref[MAXN + 5];
double a[MAXN + 5][MAXN + 5];

inline void iswp ( int& a, int& b ) { a ^= b ^= a ^= b; }
inline double dabs ( const double a ) { return a < 0 ? -a : a; }
inline int dcmp ( const double a, const double b ) {
	return dabs ( a - b ) < EPS ? 0 : ( a < b ? -1 : 1 );
}

inline bool halfp () {
	static std::mt19937 rnd ( time ( 0 ) ^ 20120712 );
	return rnd () & 1;
}

inline void pivot ( const int r, const int c ) {
	iswp ( id[r + n], id[c] );
	double tmp = -a[r][c]; a[r][c] = -1;
	rep ( i, 0, n ) a[r][i] /= tmp;
	rep ( i, 0, m ) if ( i != r && dcmp ( a[i][c], 0 ) ) {
		tmp = a[i][c], a[i][c] = 0;
		rep ( j, 0, n ) a[i][j] += tmp * a[r][j];
	}
}

inline int simplex () {
	rep ( i, 1, n ) id[i] = i;
	while ( true ) {
		int i = 0, j = 0;
		rep ( k, 1, m ) {
			if ( !~dcmp ( a[k][0], 0 ) && ( !i || halfp () ) ) {
				i = k;
			}
		}
		if ( !i ) break;
		rep ( k, 1, n ) {
			if ( dcmp ( a[i][k], 0 ) == 1 && ( !j || halfp () ) ) {
				j = k;
			}
		}
		if ( !j ) return 2;
		pivot ( i, j );
	}
	while ( true ) {
		int i = 0, j = 0;
		double mx = 0, mn = 1e9;
		rep ( k, 1, n ) if ( dcmp ( a[0][k], mx ) == 1 ) mx = a[0][j = k];
		if ( !j ) break;
		rep ( k, 1, m ) {
			if ( !~dcmp ( a[k][j], 0 ) && dcmp ( -a[k][0] / a[k][j], mn ) == -1 ) {
				mn = -a[i = k][0] / a[k][j];
			}
		}
		if ( !i ) return 1;
		pivot ( i, j );
	}
	return 0;
}

int main () {
	scanf ( "%d %d %d", &n, &m, &type );
	rep ( i, 1, n ) scanf ( "%lf", &a[0][i] );
	rep ( i, 1, m ) {
		rep ( j, 1, n ) scanf ( "%lf", &a[i][j] ), a[i][j] *= -1;
		scanf ( "%lf", &a[i][0] );
	}
	int res = simplex ();
	if ( res == 2 ) puts ( "Infeasible" );
	else if ( res ) puts ( "Unbounded" );
	else {
		printf ( "%.12f\n", a[0][0] );
		if ( type ) {
			rep ( i, 1, m ) ref[id[i + n]] = i;
			rep ( i, 1, n ) {
				printf ( "%.12f%c", ref[i] ? a[ref[i]][0] : 0, i ^ n ? ' ' : '\n' );
			}
		}
	}
	return 0;
}

posted @   Rainybunny  阅读(356)  评论(3编辑  收藏  举报
编辑推荐:
· AI与.NET技术实操系列:基于图像分类模型对图像进行分类
· go语言实现终端里的倒计时
· 如何编写易于单元测试的代码
· 10年+ .NET Coder 心语,封装的思维:从隐藏、稳定开始理解其本质意义
· .NET Core 中如何实现缓存的预热?
阅读排行:
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· ollama系列01:轻松3步本地部署deepseek,普通电脑可用
· 25岁的心里话
· 按钮权限的设计及实现
点击右上角即可分享
微信分享提示