线性规划之单纯形法

首先什么是线性规划

就是满足一些约束条件,求某个函数的最大值

zmax=x1+x2

Sit:

2x1+x2<=12

x1+2x2<=9

x1,x2>=0


如何解决:可使用单纯形法.

线性规划的可行域

满足线性规划问题约束条件的所有点组成的集合就是线性规划的可行域.

若可行有界,最优解肯定在可行域的顶点上

显然的,考虑在一个限制下的答案,肯定是一个单调的函数,那么就是在边缘去到,而把所有的限制都整合起来,最后得到的可行域,按导数的思路来说,确实是在顶点取到最优解

单纯形法就是通过设置不同的基向量,通过矩阵的线性变换,求得基可行解(可行域的顶点),并判断该解是否最优,不是最优则设置另一组基向量,直至找到最优解(现在不是很清楚是怎么求解的)


线性规划的标准形式

zmax=j=1ncjxj

Sit:

j=1nai,jxj=bj,i=1,2,3...

xj>=0,j=1,2,3...

写成矩阵形式

zmax=CX 就是C系数和X乘起来

AX=b

X>=0

A=[a1,1a1,2..a1,n::::am,1am,2..am,n]

基本形式为:

(1)目标函数取Max

(2)约束是等式

(3)x不能为负数

普通规划转标准规划

(1)zmin,那么就把统计答案符号改变,那就是目前最大值,取负号变成最小值

(2)不等式化成等式

Sit1:

x1+2x2<=6

x1+2x2+x3=6

x3>=0

Sit2:

x1+2x2>=6

x1+2x2+x3=6

x3<=0

(3)存在无限制变量

INF<=xk<=INF

xm>=0,xn>=0

INF<=xmxn<=INF

xk=xmxn

xkxm+xn=0

就可以化标准形了


几何意义

标准形中,有m个约束条件(不包括非负约束),n个决策变量,且(n>=m),这就保证了,x肯定解不出来

那么取出m个基变量

x1,x2,x3...xm

为了保证能代替全部向量,所以选出来的向量必须要可以被没选出来的向量表示出来

也不是这个意思,如果你不这么选,就可能解不出来

而且保证有解的话还不能让系数为倍数关系

而且非基向量x>=0,那么就让非基向量x=0

xi=Ci+j=m+1nmi,jxj (被非基向量表示)

xi=Ci

继续线性规划

举例:

zmax=x1+x2

Sit:

2x1+x2+x3=12

x1+2x2+x4=9

x1,x2,x3,x4>=0

选择x2,x3为基向量

另非基向量为0,方可解出

2x1+x2+x3=12

x1+2x2+x4=9

x1,x2,x3,x4>=0

那么这个求出来的点就是两个可行域的并的定点

通过不断变换基向量可找到最优顶点


如何判断是否最优

z=z0+j=m+1nkjxj

最优解的所有k都应该满足k<=0

我就说让非基向量为0的意义是什么,这样表示出来的是直线的边界

那么如果非基向量不再是非基向量,那么就不是0,也就意味着还能更大,那么必然不是最优解


重新选择基变量

k>=0的最大的k的非基向量变为基向量.


被替换向量

我们要逐渐得到更大的值,我们现在有了需要加进去的向量,那么考虑让新加进去的向量发挥更大作用,那么就得让影响最大的基向量成为非基向量.


感觉就没啥了吧,去写代码练一练。

点击查看代码
#include<iostream> #include<cstdio> #include<cstring> #include<algorithm> #include<cmath> using namespace std; typedef long long ll; const int N=25; const double eps=1e-8,INF=1e15; inline int read(){ char c=getchar();int x=0,f=1; while(c<'0'||c>'9'){if(c=='-')f=-1; c=getchar();} while(c>='0'&&c<='9'){x=x*10+c-'0'; c=getchar();} return x*f; } int n,m,type; double a[N][N],ans[N]; int id[N<<1]; int q[N]; void Pivot(int l,int e){ //传参(b是负数的位置,让他是负数的x) swap(id[n+l],id[e]); //这个时候交换就是那个基向量和非基向量 //处理一下答案位置 double t=a[l][e]; a[l][e]=1; //第一遍处理呗 for(int j=0;j<=n;j++) a[l][j]/=t; //线性变换,同除 for(int i=0;i<=m;i++) if(i!=l && abs(a[i][e])>eps){ t=a[i][e]; a[i][e]=0; //高斯消元 for(int j=0;j<=n;j++) a[i][j]-=a[l][j]*t; } } bool init(){ //这里是把第一遍确定基向量 //然后可以消元的消元 while(true){ int e=0,l=0; for(int i=1;i<=m;i++) if(a[i][0]<-eps && (!l||(rand()&1))) l=i; //这里在判断不存在满足约束的解 //当且仅当目前b是负数,并且没有负数系数的时候没有解 //否则就处理 if(!l) break; for(int j=1;j<=n;j++) if(a[l][j]<-eps && (!e||(rand()&1))) e=j; if(!e) {puts("Infeasible");return false;} Pivot(l,e); } return true; } bool simplex(){ while(true){ //就是一遍遍消元,一遍遍搞 int l=0,e=0; double mn=INF; for(int j=1;j<=n;j++) if(a[0][j]>eps) {e=j;break;} if(!e) break; for(int i=1;i<=m;i++) if(a[i][e]>eps && a[i][0]/a[i][e]<mn) mn=a[i][0]/a[i][e],l=i; if(!l) {puts("Unbounded");return false;} Pivot(l,e); } return true; } int main(){ srand(317); n=read();m=read();type=read(); for(int i=1;i<=n;i++) a[0][i]=read();//输入贡献的系数 for(int i=1;i<=m;i++){ for(int j=1;j<=n;j++) a[i][j]=read();//输入限制里的系数 a[i][0]=read(); //每一份输入b } for(int i=1;i<=n;i++) id[i]=i;//id是答案的值 if(init() && simplex()){ printf("%.8lf\n",-a[0][0]); if(type){ for(int i=1;i<=m;i++) ans[id[n+i]]=a[i][0]; for(int i=1;i<=n;i++) printf("%.8lf ",ans[i]); } } }

__EOF__

本文作者Eternal_Battle
本文链接https://www.cnblogs.com/Eternal-Battle/p/15856834.html
关于博主:这个世界除了你,都知道我喜欢你
版权声明:本博客所有文章除特别声明外,均采用 BY-NC-SA 许可协议。转载请注明出处!
声援博主:如果您觉得文章对您有帮助,可以点击文章右下角推荐一下。您的鼓励是博主的最大动力!
posted @   Gnomeshgh_k  阅读(633)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· Manus重磅发布:全球首款通用AI代理技术深度解析与实战指南
· 被坑几百块钱后,我竟然真的恢复了删除的微信聊天记录!
· 没有Manus邀请码?试试免邀请码的MGX或者开源的OpenManus吧
· 园子的第一款AI主题卫衣上架——"HELLO! HOW CAN I ASSIST YOU TODAY
· 【自荐】一款简洁、开源的在线白板工具 Drawnix
点击右上角即可分享
微信分享提示