单纯形算法(线性规划)

在讲单纯形算法前,先讲一讲线性规划

线性规划(Linear programming,简称LP)是运筹学中研究较早、发展较快、应用广泛、方法较成熟的一个重要分支,它是辅助人们进行科学管理的一种数学方法。研究线性约束条件下线性目标函数的极值问题的数学理论和方法。英文缩写LP。它是运筹学的一个重要分支,广泛应用于军事作战、经济分析、经营管理和工程技术等方面。为合理地利用有限的人力、物力、财力等资源作出的最优决策,提供科学的依据。说的通俗一点就是列方程解未知数,不过加了约束条件。

而单纯形算法是解决线性规划的一种常见的方法。在一般我们接触的数学方面的列方程解未知数一般是2个,我们可以通过在二维平面上画图求解。然而当未知数的个数多了,就很难用图像将其可视化。于是就有了单纯性算法

单纯形算法:

具体步骤是,从线性方程组找出一个个的单纯形,每一个单纯形可以求得一组解,然后再判断该解使目标函数值是增大还是变小了,决定下一步选择的单纯形。通过优化迭代,直到目标函数实现最大或最小值。 

换而言之,单纯形法就是秉承“保证每一次迭代比前一次更优”的基本思想:先找出一个基本可行解,对它进行鉴别,看是否是最优解;若不是,则按照一定法则转换到另一改进后更优的基本可行解,再鉴别;若仍不是,则再转换,按此重复进行。因基本可行解的个数有限,故经有限次转换必能得出问题的最优解。如果问题无最优解,也可用此法判别。

 

在用单纯形算法解题时最重要的是要维护一张单纯性表,如下

上个例题

问题:某食品加工厂一共有三个车间,第一车间用 1 个单位的原料 N 可以加工 5 个单位的产品 A 或 2 个单位的产品 B。产品 A 如果直接售出,售价为 10 元,如果在第二车间继续加工,则需要额外加工费 5 元,加工后售价为 19 元。产品 B 如果直接售出,售价 16 元,如果在第三车间继续加工,则需要额外加工费 4 元,加工后售价为 24 元。原材料 N 的单位购入价为 5 元,每工时的资是15 元,第一车间加工一个单位的 N,需要 0.05 个工时,第二车间加工一个单位需要 0.1 工时,第三车间加工一个单位需要 0.08工时。每个月最多能得到 12000 单位的原材料 N,工时最多为 1000 工时。如何安排生产,才能使工厂的效益最大呢?

读题后可以列个方程组

maxz=10x1+12.5x2+16x3+18.8x4-5.75x5;(不是乘1是X1)

1.x1+x2-5x5=0;

2.x3+x4-2x5=0;

3.x5≤12000;

4. 0.05x5+0.1x3+0.08x4≤1000;

5.  xi>=0  (i=1, 2, 3, 4, 5)

因为题中要求最大效益得到的maxz函数是个目标函数(就是最后题目要求的东西),而下面的5个方程是约束条件。方程中未知数大于2个,用图不好抽象,就用单纯形算法来解。

步骤:

1.标准化目标函数和约束条件

maxz=2.5x2+2.8x4+76.25x5

1.x1+x2-5x5=0;

2.x3+x4-2x5=0;

3.x5+x6=12000;0<=x6<=12000(没有精确到小区间)

4. 0.05x5+0.1x3+0.08x4+x7=1000;0<=x7<=1000

5.  xi>=0  (i=1, 2, 3, 4, 5,6,7)

 

建立一张表

出现在目标函数中的未知数我们记为非基本变量,在约束函数中只出现一次的我们记为基本变量

对表进行操作

(1)判断是否得到最优解:

①:若所有的检验数都小于零,则已获得最优解,算法结束。

②:若检验数中存在正数,而某一正数所对应的列向量的各分量都小于等于零,则线性规划问题无界,算法结束。

③:若检验数中有正数,且他们所对应的列向量有正数,则继续下面的计算。

(2)选入基变量

选取所有正检验数中最大的一个,记为 Ce,其对应的非基本变量为 Xe称为入基变量,Xe对应的列向量[a1e,a2e,…,ame]^T.为入基列.

(3) 选离基变量

选离基变量
选取“常数列元素/入基列元素”正比值的最小者,所对应的非基本变量 xk为离基变量。xk对应的行向量[ak1,ak2,…,akn]为离基行。

(4)换基变换

(5)换基变换
在单纯形表上将入基变量和离基变量互换位置,即 x3 和 x5 交换位置,换基变换之后如图 7-5 所示。

6)计算新的单纯形表

按以下方法计算新的单纯形表:
4 个特殊位置如下:
• 入基列=−原值/交叉位值(不包括交叉位)。
• 离基行=原值/交叉位值(不包括交叉位)。
• 交叉位=原值取倒数。
• c0 位=原值+同行入基列元素*同列离基行元素/交叉位值。

一般位置元素=原值−同行入基列元素*同列离基行元素/交叉位值。

得到新表

然后继续从(1)开始,直到 找到最优解或者判定无界停止。

代码

  1. #include
  2. #include
  3. #include
  4. #include
  5. using namespace std;
  6. float kernel[100][100];//存储单纯形表
  7. char FJL[100] = {};//非基本变量
  8. char JL[100] = {};//基本变量(只出现一次)
  9. int n, m, i, j;
  10. void Print()
  11. {
  12.     cout << endl;
  13.     cout << "------------------" << endl;
  14.     cout << setw(7) << "b ";
  15.     for (i = 1; i <= n; i++)
  16.     {
  17.         cout << setw(7) << "x" << FJL[i];
  18.     }
  19.     cout << endl;
  20.     cout << "c ";
  21.     for (i = 0; i <= n; i++)
  22.     {
  23.         if (i >= 1)
  24.         {
  25.             cout << "x" << JL[i];
  26.         }
  27.         for (j = 0; j <= m; j++)
  28.         {
  29.             cout << setw(7) << kernel[i][j] << " ";
  30.         }
  31.         cout << endl;
  32.     }
  33. }
  34. void DCXA()
  35. {
  36.     float max1, max2;//保存最大正非基本变量和最大正常数的基本变量
  37.     int e = -1, k = -1;//保存入基列和离基行
  38.     float min;
  39.     while (true)
  40.     {
  41.         max1 = max2 = 0;
  42.         min = 100000000;
  43.         //找入基列
  44.         for (j = 1; j <= m; j++)
  45.         {
  46.             if (max1 < kernel[0][j])
  47.             {
  48.                 max1 = kernel[0][j];
  49.                 e = j;//保存列坐标
  50.             }
  51.         }
  52.         if (max1 <= 0)//第一行的检验数全小于等于0说明找到了最优解
  53.         {
  54.             cout << endl;
  55.             cout << "最优解:" << kernel[0][0] << endl;
  56.             Print();
  57.             break;
  58.         }
  59.         for (i = 1; i <= n; i++)//找离基行
  60.         {
  61.             if (max2 < kernel[i][e])
  62.             {
  63.                 max2 = kernel[i][e];
  64.             }
  65.             float temp = kernel[i][0] / kernel[i][e];
  66.             if (temp > 0 && temp < min)
  67.             {
  68.                 min = temp;
  69.                 k = i;
  70.             }
  71.         }
  72.         cout << "入基变量:" << "x" << FJL[e] << " ";
  73.         cout << "离基变量: " << "x" << JL[k] << endl;
  74.         if (max2 == 0)
  75.         {
  76.             cout << "解无界" << endl;
  77.             break;
  78.         }
  79.         char temp = FJL[e];
  80.         FJL[e] = JL[k];
  81.         JL[k] = temp;
  82.         for (i = 0; i <= n; i++)
  83.         {
  84.             if (i != k)
  85.             {
  86.                 for (j = 0; j <= m; j++)
  87.                 {
  88.                     if (j != e)
  89.                     {
  90.                         if (i == 0 && j == 0)//c0位置
  91.                         {
  92.                             kernel[i][j] += kernel[i][e] * kernel[k][j] / kernel[k][e];
  93.                         }
  94.                         else//一般位置
  95.                         {
  96.                             kernel[i][j] -= kernel[i][e] * kernel[k][j] / kernel[k][e];
  97.                         }
  98.                     }
  99.                 }
  100.             }
  101.         }
  102.         for (i = 0; i <= n; i++)//计算入基列
  103.         {
  104.             kernel[i][e] = -kernel[i][e] / kernel[k][e];
  105.         }
  106.         for (j = 0; j <= m; j++)//计算离基列
  107.         {
  108.             kernel[k][j] = kernel[k][j] / kernel[k][e];
  109.         }
  110.         kernel[k][e] = 1 / kernel[k][e];//计算交叉列
  111.         Print();
  112.     }
  113. }
  114. int main()
  115. {
  116.     int i, j;
  117.     cin >> m;
  118.     for (i = 1; i <= m; i++)
  119.     {
  120.         cin >> FJL[i];
  121.     }
  122.     cin >> n;
  123.     for (i = 1; i <= n; i++)
  124.     {
  125.         cin >> JL[i];
  126.     }
  127.     for (i = 0; i <= n; i++)
  128.     {
  129.         for (j = 0; j <= m; j++)
  130.         {
  131.             cin >> kernel[i][j];
  132.         }
  133.     }
  134.     Print();
  135.     DCXA();
  136.     return 0;
  137. }
     

你可能感兴趣的:(算法)