线性规划之单纯形法


首先什么是线性规划

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

$z_{max}=x_1+x_2$

$Sit:$

$2x_1+x_2<=12$

$x_1+2x_2<=9$

$x_1,x_2>=0$

------------

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

线性规划的可行域

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

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

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

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

------------

线性规划的标准形式

$z_{max}=\sum_{j=1}^{n}c_jx_j$

$Sit:$

$\sum_{j=1}^{n}a_{i,j}x_j=b_j,i=1,2,3...$

$x_j>=0,j=1,2,3...$

写成矩阵形式

$z_{max}=CX$      就是$C$系数和$X$乘起来

$AX=b$

$X>=0$

$A=\begin{bmatrix}
    a_{1,1} & a_{1,2} &.. & a_{1,n}\\
    : & : & : & :\\
    a_{m,1} & a_{m,2} &.. & a_{m,n}\\
\end{bmatrix}$

基本形式为$:$

$(1)$目标函数取$Max$

$(2)$约束是等式

$(3)x$不能为负数

普通规划转标准规划

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

$(2)$不等式化成等式

$Sit1:$

$x_1+2x_2<=6$

$x_1+2x_2+x_3=6$

$x_3>=0$

$Sit2:$

$x_1+2x_2>=6$

$x_1+2x_2+x_3=6$

$x_3<=0$

$(3)$存在无限制变量

$-INF<=x_k<=INF$

$x_m>=0,x_n>=0$

$-INF<=x_m-x_n<=INF$

$x_k=x_m-x_n$

$x_k-x_m+x_n=0$

就可以化标准形了

------

几何意义

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

那么取出$m$个基变量

$x_1,x_2,x_3...x_m$

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

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

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

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

$x_i=C_i+\sum_{j=m+1}^{n}m_{i,j}x_j$    (被非基向量表示)

$x_i=C_i$

继续线性规划

举例$:$

$z_{max}=x_1+x_2$

$Sit:$

$2x_1+x_2+x_3=12$

$x_1+2x_2+x_4=9$

$x_1,x_2,x_3,x_4>=0$

选择$x_2,x_3$为基向量

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

$2x_1+x_2+x_3=12$

$x_1+2x_2+x_4=9$

$x_1,x_2,x_3,x_4>=0$

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

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

------

如何判断是否最优

$z=z_0+\sum_{j=m+1}^{n}k_jx_j$

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

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

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

------

重新选择基变量

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

------

被替换向量

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

------

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


#include
#include
#include
#include
#include
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]);
        }
    }
}