シンプレックス・タブロー
シンプレックス・タブローで線形計画を解くコードです。
自分の理解のため作りました。見れ分かると思いますが、適当です。
制約条件を<=にする場合は適当に補助変数を足してください。
教科書に載っているような3×3程度の問題ならこのコードで解けますが、
大規模(10000列とか)になると掃き出していくうちに数値誤差が累積するのか
縮退・巡回しまくって収束しないです。==0 の条件を緩めるとか、Bland則・ピボットを
ランダム選択するなどをしないと駄目なようです。
でも、そうやるのも大変なのではっきりいって、線形計画パッケージのlp_solveを使ったほうがいいです。
lp_solveは拡張シンプレックス法(良く分からないが)を使って、整数計画問題にも対応していて
分枝限定法を使うらしいです。lp_solveは初めて使いましたが、簡単だし、ちゃんと答えもでるし、
優れものです。大規模線形計画コードは自作できそうにないので助かります。
// 目的関数:z = c^T x, 制約条件:Ax = b をタブロー法で解く // A:M行N列 double solve_simplex_tableau(double *A, double *b, double * c, double *x, int N, int M) { double z0; double *tmp; tmp = new double[(M + 1) * (N + 1)]; memset(tmp, 0, sizeof(double) * (M + 1) * (N + 1)); int i, j; for(i = 0;i < M;i++) { for(j = 0;j < N;j++) { tmp[(i+1) * (N+1) + j] = A[i * N + j]; } tmp[(i+1) * (N+1) + j] = b[i]; } // 最上列 for(j = 0;j < N;j++) tmp[j] = -c[j]; int it; for(it = 0;it < 1000;it++){ // 最上列から最小要素となる列を選択 double min_z, min_c; int min_i, min_j; min_z = 0.0; for(j = 0;j < N;j++) { if(min_z > tmp[j]) { min_z = tmp[j]; min_j = j; } } // 最上列に負の要素がなくなれば修了 if(min_z == 0) { z0 = tmp[N]; for(i = 0;i < N;i++) x[i] = tmp[(i+1)*(N+1)+N]; goto END_LOOP; } // 最小列の要素で定数項を割り、定数項が最小となる行を探す min_c = 100000.0; for(i = 0;i < M;i++) { double p = tmp[(i+1) * (N+1) + N] / tmp[(i+1) * (N+1) + min_j]; if(p < min_c) { min_c = p; min_i = (i+1); } } // (min_i, min_j)をピボットとして掃き出し実行 double a; a = tmp[min_i * (N+1) + min_j]; for(j = 0;j <= N;j++) tmp[min_i * (N+1) + j] /= a; for(i = 0;i <= M;i++) { if(i == min_i) continue; a = tmp[i * (N+1) + min_j]; for(j = 0;j <= N;j++) tmp[i * (N+1) + j] -= a * tmp[min_i * (N+1) + j]; }; }; END_LOOP: delete tmp; return z0; }