Skip to content

Commit eae8dad

Browse files
committed
Simplex法 randomizeしてワーストケース回避
1 parent f0569a7 commit eae8dad

File tree

2 files changed

+40
-4
lines changed

2 files changed

+40
-4
lines changed

combinatorial_opt/simplex.hpp

Lines changed: 34 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,19 @@
11
#pragma once
2+
#include <algorithm>
3+
#include <chrono>
24
#include <cmath>
35
#include <numeric>
6+
#include <random>
47
#include <vector>
58

69
// CUT begin
710
// Maximize cx s.t. Ax <= b, x >= 0
811
// Implementation idea: https://kopricky.github.io/code/Computation_Advanced/simplex.html
912
// Refer to https://hitonanode.github.io/cplib-cpp/combinatorial_opt/simplex.hpp
10-
template <typename Float = double, int DEPS = 30> struct Simplex {
13+
template <typename Float = double, int DEPS = 30, bool Randomize = true> struct Simplex {
1114
const Float EPS = Float(1.0) / (1LL << DEPS);
1215
int N, M;
16+
std::vector<int> shuffle_idx;
1317
std::vector<int> idx;
1418
std::vector<std::vector<Float>> mat;
1519
int i_ch, j_ch;
@@ -35,7 +39,7 @@ template <typename Float = double, int DEPS = 30> struct Simplex {
3539
inline Float abs_(Float x) noexcept { return x > -x ? x : -x; }
3640
void _solve() {
3741
std::vector<int> jupd;
38-
for (j_ch = N;;) {
42+
for (nb_iter = 0, j_ch = N;; nb_iter++) {
3943
if (i_ch < M) {
4044
std::swap(idx[j_ch], idx[i_ch + N + 1]);
4145
mat[i_ch][j_ch] = Float(1) / mat[i_ch][j_ch];
@@ -90,11 +94,38 @@ template <typename Float = double, int DEPS = 30> struct Simplex {
9094
}
9195

9296
public:
93-
Simplex(const std::vector<std::vector<Float>> &A, const std::vector<Float> &b, const std::vector<Float> &c) {
97+
Simplex(std::vector<std::vector<Float>> A, std::vector<Float> b, std::vector<Float> c) {
9498
is_infty = infeasible = false;
99+
100+
if (Randomize) {
101+
std::mt19937 rng(std::chrono::steady_clock::now().time_since_epoch().count());
102+
103+
std::vector<std::pair<std::vector<Float>, Float>> Abs;
104+
for (unsigned i = 0; i < A.size(); i++) Abs.emplace_back(A[i], b[i]);
105+
std::shuffle(Abs.begin(), Abs.end(), rng);
106+
A.clear(), b.clear();
107+
for (auto &&Ab : Abs) A.emplace_back(Ab.first), b.emplace_back(Ab.second);
108+
109+
shuffle_idx.resize(c.size());
110+
std::iota(shuffle_idx.begin(), shuffle_idx.end(), 0);
111+
std::shuffle(shuffle_idx.begin(), shuffle_idx.end(), rng);
112+
auto Atmp = A;
113+
auto ctmp = c;
114+
for (unsigned i = 0; i < A.size(); i++) {
115+
for (unsigned j = 0; j < A[i].size(); j++) A[i][j] = Atmp[i][shuffle_idx[j]];
116+
}
117+
for (unsigned j = 0; j < c.size(); j++) c[j] = ctmp[shuffle_idx[j]];
118+
}
119+
95120
_initialize(A, b, c);
96121
_solve();
122+
123+
if (Randomize) {
124+
auto xtmp = x;
125+
for (unsigned j = 0; j < c.size(); j++) x[shuffle_idx[j]] = xtmp[j];
126+
}
97127
}
128+
unsigned nb_iter;
98129
bool is_infty;
99130
bool infeasible;
100131
std::vector<Float> x;

combinatorial_opt/simplex.md

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -59,14 +59,19 @@ $
5959

6060
- ひとまずその時点で $c_j$ が正となるものを選ぶ.
6161
- 複数存在する場合は添字番号最小(Bland's rule)
62-
- $\mathbf{c}$ の全要素が $0$ ならこれ以上解が改善しないので終了
62+
- $\mathbf{c}$ の全要素が $0$ ならこれ以上解が改善しないので終了
6363

6464
#### $y_i$ の決定
6565

6666
- 最も不等式制約が厳しいものを選ぶ.
6767
- さもなくば原点が実行可能領域から飛び出るので
6868
- 複数存在する場合は添字番号最小(Bland's rule)
6969

70+
#### ワーストケースの回避
71+
72+
- 指数回のステップを要する恣意的なケースが知られている.
73+
- 特に [1] の p.43 (2.71) で挙げられているケースに関しては,変数・制約の順序をランダムに取り換えることで回避が可能であることを経験的に確認したので,デフォルトでこれを行うことにした.
74+
7075
## 問題例
7176

7277
- [AtCoder jag2018summer-day3 H - Enlarge Circles](https://atcoder.jp/contests/jag2018summer-day3/tasks/jag2018summer_day3_h)

0 commit comments

Comments
 (0)