线性规划之单纯性算法

标签: 数学
4207人阅读 评论(0) 收藏 举报
分类:
线性函数:按比例,一阶导数为常数的函数。
松弛变量:引入新的变量,且保持变量间的关系不变。
提到线性规划,自己最先想起的是高中的各种数学题。
线性规划问题的标准形式:

用松弛变量技术把不等式化成等式:


单纯性算法的思想:
从一个基本可行解出发,寻找能使得目标函数更优的另一个解,迭代下去直到找不到。使用这种方法避免大量的无用比较。
例子:
FOREVER284LOVE的线性规划与单纯形法》
看到的一题,以它为例:
某工厂在计划期内要安排生产I、  II两种产品, 已知生产单位产品所需的设备台时及A、B两种原材 料的损耗,如表所示:


        I

       II


      设备

  原材料A

  原材料B

        1

        4

        0

       2

       0

       4

   8台时

    16kg

    12kg

该工厂每生产一件产品Ⅰ可获利2元,每生产一件产品Ⅱ可获利3元,问应如何安排计划使该工厂获利最多?
解:列出标准形式:

所以:

称Z表达式中的变量为非基变量(x1,x2),约束方程中剩余的变量是基变量 (x3,x4,x5)
当非基变量全是0时,Z有一个X解。这一个解作为初始解。想要让Z变大,那么正整数系数的变量一定要变大。
可以选一个最大系数的变量x2,那么x1随着x2变大将变成0,那么从约束方程可以看出,X5先变成0,故把X1和X5作为新的非基变量。
有:

存在正系数说明目标函数还能变大。令x5=0,从约束方程可以看出,x1最大取2,此时x3=0。那么新的非基变量就是x3, x5.于是得到:

重复刚刚的步骤,得到:

当x3=x4=0时得到最优解z=14
把刚刚的过程表格化:(表格化思想来自《组合数学及应用》)



我们发现,事实上,一个基本变量和一个非基本变量互换后得到了下一个方程组。
寻找互换的非基本变量:对应的Z行系数是负数的,选择它,有多个负数就选择最小的那个。对应的非基本变量就是选择项。
寻找互换的基本变量:非基本变量增大,基本变量减小,谁先变成0,谁就是基本变量。
数值元素变化规律:称参与互换的非基变量所在列是主元列,基本变量所在行是主元行。
设当前的主元行是r,主元列是c,主元为, 各个位置的元素值是

设投影列元素a,投影行元素b。投影是相对主元行和主元列而言,上图中16的投影行元素是12,投影列元素是0
那么其他的元素就是:
后续变化:







嗯嗯,就是这个流程。

设那些有数字元素构成一个二维矩阵a[][]. 设n个变量,m个约束条件。
那么修改数字矩阵的部分写成程序就是 
    a[r][c] = 1 / a[r][c];
    for(int j = 0; j <= n; j++) if(j != c) a[r][j] *= a[r][c];
    for(int i = 0; i <= m; i++)
    if(i != r) {
      for(int j = 0; j <= n; j++)
        if(j != c) a[i][j] -= a[i][c] * a[r][j];
      a[i][c] = -a[i][c] * a[r][c];
    }
寻找基本变量和非基本变量,判断有界、无解、无界部分是真不会写了,参考别人的模板。

献上一题:
uva 10498 - Happiness
大意:买N件物品,每个人对每件物品有自己的满意度,且有一个满意度和的限制。每一件物品有自己的价格,问能花费的最多的钱。

#include <iostream>
#include <cstdio>
#include <cmath>
using namespace std;
const int maxm = 500; // 约束数目上限
const int maxn = 500; // 变量数目上限
const double INF = 1e100;
const double eps = 1e-10;

struct Simplex {
  int n; // 变量个数
  int m; // 约束个数
  double a[maxm][maxn]; // 输入矩阵
  int B[maxm], N[maxn]; // 算法辅助变量

  void calc(int r, int c) {
    swap(N[c], B[r]);
    a[r][c] = 1 / a[r][c];
    for(int j = 0; j <= n; j++) if(j != c) a[r][j] *= a[r][c];
    for(int i = 0; i <= m; i++)
    if(i != r) {
      for(int j = 0; j <= n; j++)
        if(j != c) a[i][j] -= a[i][c] * a[r][j];
      a[i][c] = -a[i][c] * a[r][c];
    }
  }

  bool check() {
    while(1) {
      int r, c;
      double p = INF;
      for(int i = 0; i < m; i++){
         if(a[i][n] < p) {
            r=i;
            p = a[r][n];  // 最右边的常数列
         }
      }
      if(p > -eps) return true;  // 都是正数的话可行。
      p = 0;
      for(int i = 0; i < n; i++) {
          if(a[r][i] < p){
             c=i;
             p = a[r][c];  // 找最小列
          }
      }
      if(p > -eps) return false;  // r对应的所有列都是正数,没有负数,无解。
      p = a[r][n]/a[r][c];
      for(int i = r+1; i < m; i++)
        if(a[i][c] > eps) {
           double v = a[i][n] / a[i][c];
           if(v < p) {
               r = i;
               p = v;
           }
      }
      calc(r, c);
    }
  }

  // 解有界返回1,无解返回0,无界返回-1。b[i]为x[i]的值,ret为目标函数的值
  int simplex(int n, int m, double x[maxn], double& ret) {
    this->n = n;
    this->m = m;
    for(int i = 0; i < n; i++) N[i] = i;
    for(int i = 0; i < m; i++) B[i] = n+i;
    if(!check()) return 0;
    while(1){
      int r, c;
      double p = 0;
      for(int i = 0; i < n; i++) if(a[m][i] > p) {
            c = i;
            p = a[m][c];
      }
      if(p < eps) {
        for(int i = 0; i < n; i++) if(N[i] < n) x[N[i]] = 0;
        for(int i = 0; i < m; i++) if(B[i] < n) x[B[i]] = a[i][n];
        ret = -a[m][n];
        return 1;
      }
      p = INF;
      for(int i = 0; i < m; i++) if(a[i][c] > eps) {
        double v = a[i][n] / a[i][c];
        if(v < p) { r = i; p = v; }
      }
      if(p == INF) return -1;
      calc(r, c);
    }
  }
};

Simplex solver;
int main()
{
    //freopen("cin.txt","r",stdin);
    int n, m;
    while(~scanf("%d%d", &n, &m)) {
        for(int i = 0; i < n; i++) scanf("%lf", &solver.a[m][i]); // 目标函数
        solver.a[m][n] = 0;
        for(int i = 0; i < m; i++)
           for(int j = 0; j < n+1; j++)
               scanf("%lf", &solver.a[i][j]);
        double ans,x[maxn];
        solver.simplex(n,m,x,ans);
        ans *= m;
        //printf("Nasa can spend %d taka.\n", int(ans+0.5)); // 至少花费的:3.008-->4
        printf("Nasa can spend %d taka.\n", (int)floor(ans + 1 - eps));
    }
    return 0;
}

查看评论

算法导论之线性规划

线性规划,充斥着运筹学,在图的单源最短路径求解差分约束系统就是用到线性规划。怎么样问题可以建模为线性规划来解决呢?在给定的有限的资源和竞争约束情况下,取得最大化或最小化目标的问题。导论中给出政治竞选问...
  • fjssharpsword
  • fjssharpsword
  • 2016-11-17 09:21:32
  • 5918

线性规划:单纯形算法

作者 dylanFrank(滔滔) 转载请联系作者 原文链接:http://blog.csdn.net/Dylan_Frank/article/details/77876006 这里简要总结一下线...
  • Dylan_Frank
  • Dylan_Frank
  • 2017-09-07 00:18:58
  • 1362

【高级算法】单纯形法求解线性规划问题(C++实现)

1 单纯形法(1) 单纯形法是解线性规划问题的一个重要方法。 其原理的基本框架为: 第一步:将LP线性规划变标准型,确定一个初始可行解(顶点)。 第二步:对初始基可行解最优性判别,若最优,停止...
  • zhoubin1992
  • zhoubin1992
  • 2015-07-16 19:40:28
  • 8364

线性规划算法原理介绍

线性规划定义: 求满足约束的最优目标,目标是变量的线性函数,约束是变量的相等或不等表达式。 单纯形算法 1 松弛变量 为将不等式转化为等式添加的非负变量 比如 将f(xi) >0 变...
  • mxlwd168
  • mxlwd168
  • 2017-01-11 21:35:39
  • 343

线性规划算法

  • 2012年11月01日 11:25
  • 188KB
  • 下载

线性规划和约束满足问题的思考

本文写给对线性规划和约束满足问题的使用有困惑的朋友,如果你曾经在这方面存在一些疑问,这篇文章对你来说就再适合不过了,如果有对线性规划的解法感兴趣,那么也推荐你看一看我的思考~ *注:之前一直以为约束...
  • yin_wuzhe
  • yin_wuzhe
  • 2016-09-18 11:19:41
  • 6590

线性规划(算法导论)

花了将近四天的时间初略的把>线性规划的一章的伪代码实现了,调试的时间略长,当中遇到很多的问题,只有自己动手才会体会的更深。虽然调试出来了,但代码还很不精简,以后有时间来优化一下。        #i...
  • ysja_1990
  • ysja_1990
  • 2015-12-10 10:34:44
  • 297

线性规划

原文地址:http://blog.sina.com.cn/s/blog_61e8042b0100eepi.html 在数学中,线性规划 (Linear Programming,简称L...
  • xiaogugood
  • xiaogugood
  • 2014-01-14 17:12:59
  • 15362

单纯形算法求解线性规划问题(依据《算法导论》实现)

#include #include #include #include #include using namespace std; const int MAXN = 100; const ...
  • niuxiunan
  • niuxiunan
  • 2015-11-30 16:05:30
  • 1686

算法大全_第01章__线性规划

  • 2014年07月05日 16:02
  • 177KB
  • 下载
    个人资料
    持之以恒
    等级:
    访问量: 37万+
    积分: 9440
    排名: 2419
    我的链接
    最新评论