gist.bido.dev

simplex シンプレックス法

### simplex.rs

struct Simplex {
    tableau: Vec<Vec<f64>>, // シンプレックス表
    m: usize,               // 制約式の数
    n: usize,               // 変数の数
}

impl Simplex {
    // A: 制約行列, b: 右辺定数ベクトル, c: 目的関数の係数ベクトル
    // offset: 目的関数の定数項
    fn new(a: Vec<Vec<f64>>, b: Vec<f64>, c: Vec<f64>, offset: f64) -> Self {
        let m = a.len();
        let n = c.len();
        // 表のサイズ: (行: m + 1) x (列: n + m + 1)
        // 列構成: [変数x | スラック変数s | 右辺RHS]
        let mut tableau = vec![vec![0.0; n + m + 1]; m + 1];

        for i in 0..m {
            for j in 0..n {
                tableau[i][j] = a[i][j];
            }
            tableau[i][n + i] = 1.0; // スラック変数の係数
            tableau[i][n + m] = b[i]; // RHS
        }

        for j in 0..n {
            tableau[m][j] = -c[j]; // 目的関数 (最大化のため符号反転)
        }
        tableau[m][n + m] = offset; // 定数項をRHSの初期値にセット

        Simplex { tableau, m, n }
    }

    fn solve(&mut self) -> Result<f64, String> {
        loop {
            // 1. ピボット列の選択 (目的関数行で最も負の要素)
            let mut pivot_col = 0;
            let mut min_val = 0.0;
            for j in 0..(self.n + self.m) {
                if self.tableau[self.m][j] < min_val {
                    min_val = self.tableau[self.m][j];
                    pivot_col = j;
                }
            }

            // 最適性の判定: 負の係数がなければ終了
            if min_val >= -1e-9 {
                break;
            }

            // 2. ピボット行の選択 (最小比テスト)
            let mut pivot_row = None;
            let mut min_ratio = f64::INFINITY;

            for i in 0..self.m {
                if self.tableau[i][pivot_col] > 1e-9 {
                    let ratio = self.tableau[i][self.n + self.m] / self.tableau[i][pivot_col];
                    if ratio < min_ratio {
                        min_ratio = ratio;
                        pivot_row = Some(i);
                    }
                }
            }

            let p_row = match pivot_row {
                Some(r) => r,
                None => return Err("問題は非有界(Unbounded)です。".to_string()),
            };

            // 3. ピボット操作 (ガウスの消去法)
            self.pivot(p_row, pivot_col);
        }
        Ok(self.tableau[self.m][self.n + self.m])
    }

    fn pivot(&mut self, row: usize, col: usize) {
        let divisor = self.tableau[row][col];
        for j in 0..=(self.n + self.m) {
            self.tableau[row][j] /= divisor;
        }

        for i in 0..=self.m {
            if i != row {
                let multiplier = self.tableau[i][col];
                for j in 0..=(self.n + self.m) {
                    self.tableau[i][j] -= multiplier * self.tableau[row][j];
                }
            }
        }
    }

    fn print_tableau(&self) {
        println!("--- Tableau ---");
        for row in &self.tableau {
            for val in row {
                print!("{:>8.2} ", val);
            }
            println!();
        }
    }
}

fn main() {
    println!("Solving 0-1 Integer Programming Problem:");
    println!("Maximize: Z = 3x1 + 4x2 + x3 + 2x4");
    println!("Subject to: 2x1 + 3x2 + x3 + 3x4 <= 4");
    println!("x_i in {{0, 1}} for i in {{1, 2, 3, 4}}\n");

    let c = vec![3.0, 4.0, 1.0, 2.0]; // 係数 (x1, x2, x3, x4)
    let a = vec![2.0, 3.0, 1.0, 3.0]; // 制約の係数
    let b = 4.0;                     // 制約の右辺

    // 1. 0-1 整数計画問題として解く (全探索)
    let mut max_z_int = -1.0;
    let mut best_x_int = vec![0; 4];

    for i in 0..(1 << 4) {
        let mut current_z = 0.0;
        let mut current_weight = 0.0;
        let mut x = vec![0; 4];
        for j in 0..4 {
            if (i >> j) & 1 == 1 {
                current_z += c[j];
                current_weight += a[j];
                x[j] = 1;
            }
        }

        if current_weight <= b {
            if current_z > max_z_int {
                max_z_int = current_z;
                best_x_int = x;
            }
        }
    }

    println!("--- 0-1 Integer Solution ---");
    println!("Optimal Z: {:.1}", max_z_int);
    for (i, val) in best_x_int.iter().enumerate() {
        println!("x{} = {}", i + 1, val);
    }
    println!();

    // 2. 線形緩和問題 (Continuous Relaxation) をシンプレックス法で解く
    println!("--- Continuous Relaxation (Simplex Method with xi <= 1) ---");
    let a_matrix = vec![
        vec![2.0, 3.0, 1.0, 3.0], // 主制約
        vec![1.0, 0.0, 0.0, 0.0], // x1 <= 1
        vec![0.0, 1.0, 0.0, 0.0], // x2 <= 1
        vec![0.0, 0.0, 1.0, 0.0], // x3 <= 1
        vec![0.0, 0.0, 0.0, 1.0], // x4 <= 1
    ];
    let b_vec = vec![4.0, 1.0, 1.0, 1.0, 1.0];
    let c_vec = vec![3.0, 4.0, 1.0, 2.0];
    let offset = 0.0;

    let mut simplex = Simplex::new(a_matrix, b_vec, c_vec, offset);
    match simplex.solve() {
        Ok(res) => {
            println!("Optimal Z (Upper Bound): {:.4}", res);
            println!("\nFinal Tableau for Relaxation:");
            simplex.print_tableau();
        }
        Err(e) => println!("Error: {}", e),
    }
}