《算法》第六章部分程序 part 8

▶ 书中第六章部分程序,加上自己补充的代码,包括单纯形法求解线性规划问题

● 单纯形法求解线性规划问题

  1 // 表上作业法,I 为单位阵,y 为对偶变量,z 为目标函数值
  2 //                            n         m     1
  3 //                      ┌───────────┬───────┬───┐
  4 //                      │           │       │   │
  5 //                    m │     A     │   I   │ b │
  6 //  a[m+1][n+m+1] =     │           │       │   │
  7 //                      ├───────────┼───────┼───┤
  8 //                    1 │     c     │   y   │ z │
  9 //                      └───────────┴───────┴───┘
 10 
 11 package package01;
 12 
 13 import edu.princeton.cs.algs4.StdOut;
 14 import edu.princeton.cs.algs4.StdRandom;
 15 
 16 public class class01
 17 {
 18     private static final double EPSILON = 1.0E-10;
 19     private double[][] a;                   // 工作表
 20     private int m;                          // 约束数
 21     private int n;                          // 变量数
 22     private int[] basis;                    // basis[p] = q,第 p 行的基变量是 x[q]
 23 
 24     public class01(double[][] A, double[] b, double[] c)
 25     {
 26         m = b.length;
 27         n = c.length;
 28         for (int i = 0; i < m; i++)
 29         {
 30             if (b[i] < 0)                   // 要求 b 的分量均不小于 0
 31                 throw new IllegalArgumentException("RHS must be nonnegative");
 32         }
 33         a = new double[m + 1][n + m + 1];
 34         for (int i = 0; i < m; i++)         // a[0:m-1][0:n-1] = A(两边包含)
 35         {
 36             for (int j = 0; j < n; j++)
 37                 a[i][j] = A[i][j];
 38         }
 39         for (int i = 0; i < m; i++)         // a[0:m-1][n:n+m-1] = I_m
 40             a[i][n + i] = 1.0;
 41         for (int i = 0; i < n; i++)         // a[m][0:n-1] = c
 42             a[m][i] = c[i];
 43         for (int i = 0; i < m; i++)         // a[0:m-1][n+m] = b
 44             a[i][m + n] = b[i];
 45         basis = new int[m];
 46         for (int i = 0; i < m; i++)
 47             basis[i] = n + i;
 48 
 49         for (;;)                            // 单纯形法求解
 50         {
 51             int q = -1;
 52             for (int i = 0; q == -1 && i < m + n; i++)  // 找到可优化的变量对应的列 q,即 c[j] > 0 的最靠前索引
 53                 q = (a[m][i] > 0) ? i : q;
 54             if (q == -1)                    // 优化已完成
 55                 break;
 56 
 57             int p = -1;                     // 依最小比例规则选择离开向量 p
 58             for (int i = 0; i < m; i++)     // 要求 a[i][q] > 0 且要么 p 选第一个,要么选壁纸最大的那个
 59             {
 60                 if (a[i][q] > EPSILON && (p == -1 || (a[i][m + n] / a[i][q]) < (a[p][m + n] / a[p][q])))
 61                     p = i;
 62             }
 63             if (p == -1)                    // 无离开向量,无界解
 64                 throw new ArithmeticException("Linear program is unbounded");
 65 
 66             for (int i = 0; i <= m; i++)    // 对其他行使用a[p][q] 进行高斯消元
 67             {
 68                 for (int j = 0; j <= m + n; j++)
 69                 {
 70                     if (i != p && j != q)
 71                         a[i][j] -= a[p][j] * a[i][q] / a[p][q];
 72                 }
 73             }
 74             for (int i = 0; i <= m; i++)    // 消元后其他行清零(消除浮点计算误差)
 75             {
 76                 if (i != p)
 77                     a[i][q] = 0.0;
 78             }
 79             for (int j = 0; j <= m + n; j++)// 主元所在行归一化
 80             {
 81                 if (j != q)
 82                     a[p][j] /= a[p][q];
 83             }
 84             a[p][q] = 1.0;
 85 
 86             basis[p] = q;                   // 更新基向量
 87         }
 88         assert check(A, b, c);              // 检查结果
 89     }
 90 
 91     private int dantzig()                   // 搜索 c 中值大于零的、最靠前的项的索引
 92     {
 93         int q = 0;
 94         for (int j = 1; j < m + n; j++)
 95         {
 96             if (a[m][j] > a[m][q])
 97                 q = j;
 98         }
 99         return a[m][q] <= 0 ? -1 : q;       // c 中各项均小于0,优化已完成
100     }
101 
102     public double value()                   // 最优解的目标函数值
103     {
104         return -a[m][m + n];
105     }
106 
107     public double[] primal()                // 最优解的自变量值
108     {
109         double[] x = new double[n];
110         for (int i = 0; i < m; i++)
111         {
112             if (basis[i] < n)
113                 x[basis[i]] = a[i][m + n];
114         }
115         return x;
116     }
117 
118     public double[] dual()                  // 最优解的对偶变量值
119     {
120         double[] y = new double[m];
121         for (int i = 0; i < m; i++)
122             y[i] = -a[m][n + i];
123         return y;
124     }
125 
126     private boolean isPrimalFeasible(double[][] A, double[] b)  // 解的松弛性
127     {
128         double[] x = primal();
129         for (int j = 0; j < x.length; j++)                      // 检查最优解分量是否均非负
130         {
131             if (x[j] < 0.0)
132             {
133                 StdOut.println("x[" + j + "] = " + x[j] + " is negative");
134                 return false;
135             }
136         }
137         for (int i = 0; i < m; i++)                             // 检查约束条件
138         {
139             double sum = 0.0;
140             for (int j = 0; j < n; j++)
141                 sum += A[i][j] * x[j];
142             if (sum > b[i] + EPSILON)
143             {
144                 StdOut.println("not primal feasibleb
b[" + i + "] = " + b[i] + ", sum = " + sum);
145                 return false;
146             }
147         }
148         return true;
149     }
150 
151     private boolean isDualFeasible(double[][] A, double[] c)    // 对偶解的松弛性
152     {
153         double[] y = dual();
154         for (int i = 0; i < y.length; i++)                      // 检查对偶变量不小于 0
155         {
156             if (y[i] < 0.0)
157             {
158                 StdOut.println("y[" + i + "] = " + y[i] + " is negative");
159                 return false;
160             }
161         }
162         for (int j = 0; j < n; j++)                             // 检查对偶约束条件 A*y ≥ c
163         {
164             double sum = 0.0;
165             for (int i = 0; i < m; i++)
166                 sum += A[i][j] * y[i];
167             if (sum < c[j] - EPSILON)
168             {
169                 StdOut.println("not dual feasible
c[" + j + "] = " + c[j] + ", sum = " + sum);
170                 return false;
171             }
172         }
173         return true;
174     }
175 
176     private boolean isOptimal(double[] b, double[] c)           // 检查解的最优性 value == c*x == y*b
177     {
178         double[] x = primal(), y = dual();
179         double value = value(), value1 = 0.0;
180         for (int j = 0; j < x.length; j++)
181             value1 += c[j] * x[j];
182         double value2 = 0.0;
183         for (int i = 0; i < y.length; i++)
184             value2 += y[i] * b[i];
185         if (Math.abs(value - value1) > EPSILON || Math.abs(value - value2) > EPSILON)
186         {
187             StdOut.println("value = " + value + ", cx = " + value1 + ", yb = " + value2);
188             return false;
189         }
190         return true;
191     }
192 
193     private boolean check(double[][]A, double[] b, double[] c)
194     {
195         return isPrimalFeasible(A, b) && isDualFeasible(A, c) && isOptimal(b, c) && dantzig() == -1;
196     }
197 
198     private void show()                                         // 输出工作表
199     {
200         StdOut.println("m = " + m);
201         StdOut.println("n = " + n);
202         for (int i = 0; i <= m; i++)
203         {
204             for (int j = 0; j <= m + n; j++)
205                 StdOut.printf("%7.2f ", a[i][j]);
206             StdOut.println();
207         }
208         StdOut.println("value = " + value());
209         for (int i = 0; i < m; i++)
210         {
211             if (basis[i] < n)
212                 StdOut.println("x_" + basis[i] + " = " + a[i][m + n]);
213         }
214         StdOut.println();
215     }
216 
217     private static void test(double[][] A, double[] b, double[] c)  // 测试函数
218     {
219         class01 lp;
220         try                                                         // 有解正常输出,无解用异常提前退出
221         {
222             lp = new class01(A, b, c);
223         }
224         catch (ArithmeticException e)
225         {
226             System.out.println(e);
227             return;
228         }
229         StdOut.println("value = " + lp.value());
230         double[] x = lp.primal();
231         for (int i = 0; i < x.length; i++)
232             StdOut.println("x[" + i + "] = " + x[i]);
233         double[] y = lp.dual();
234         for (int j = 0; j < y.length; j++)
235             StdOut.println("y[" + j + "] = " + y[j]);
236     }
237 
238     private static void test1()
239     {
240         double[][] A = { { -1,  1,  0 },{ 1,  4,  0 },{ 2,  1,  0 },{ 3, -4,  0 },{ 0,  0,  1 } };
241         double[] b = { 5, 45, 27, 24, 4 }, c = { 1, 1, 1 };
242         test(A, b, c);
243     }
244 
245     private static void test2() // x[0] = 12, x[1] = 28, value = 800
246     {
247         double[][] A = { { 5.0, 15.0 },{ 4.0,  4.0 },{ 35.0, 20.0 } };
248         double[] b = { 480.0, 160.0, 1190.0 }, c = { 13.0,  23.0 };
249         test(A, b, c);
250     }
251 
252     private static void test3() // unbounded
253     {
254         double[][] A = { { -2.0, -9.0,  1.0,  9.0 },{ 1.0,  1.0, -1.0, -2.0 } };
255         double[] b = { 3.0,   2.0 }, c = { 2.0, 3.0, -1.0, -12.0 };
256         test(A, b, c);
257     }
258 
259 
260     private static void test4() // x[0] = 1.0, x[1] = 0.0, x[2] = 1.0, x[3] = 0.0, value = 1.0,周期
261     {
262         double[][] A = { { 0.5, -5.5, -2.5, 9.0 },{ 0.5, -1.5, -0.5, 1.0 },{ 1.0,  0.0,  0.0, 0.0 } };
263         double[] b = { 0.0,   0.0,  1.0 }, c = { 10.0, -57.0, -9.0, -24.0 };
264         test(A, b, c);
265     }
266 
267     public static void main(String[] args)
268     {
269         StdOut.println("----- test 1 --------------------");
270         test1();
271         StdOut.println("
----- test 2 --------------------");
272         test2();
273         StdOut.println("
----- test 3 --------------------");
274         test3();
275         StdOut.println("
----- test 4 --------------------");
276         test4();
277         StdOut.println("
----- test random ---------------");              // 随机测试
278         int m = Integer.parseInt(args[0]), n = Integer.parseInt(args[1]);
279         double[][] A = new double[m][n];
280         double[] b = new double[m], c = new double[n];
281         for (int i = 0; i < m; i++)
282         {
283             for (int j = 0; j < n; j++)
284                 A[i][j] = StdRandom.uniform(100);
285         }
286         for (int i = 0; i < m; i++)
287             b[i] = StdRandom.uniform(1000);
288         for (int j = 0; j < n; j++)
289             c[j] = StdRandom.uniform(1000);
290         class01 lp = new class01(A, b, c);
291         test(A, b, c);
292     }
293 }
原文地址:https://www.cnblogs.com/cuancuancuanhao/p/10066584.html