締め切り駆動開発

チラシの裏です.日記代わりに研究や趣味や日常のことを書きます.

整数計画法による定式化テクニックをまとめたい

自分用. 定式化するとき同じことを無限回調べてる気がするのでまとめる. 随時更新.

全人類が読むべき参考文献

安直に「ILP 定式化」とかで調べると素晴らしい資料が無限個見つかる. 特にお世話になっているものは以下.

整数線形計画問題(ILP: Integer Linear Programming problem)

変数が整数に制約された線形計画問題. 簡単のために変数がすべて0か1の二値だとすると,こんな感じ:


\begin{array}{cll}
&\mathrm{maximize} &\displaystyle \sum_{n=1}^{N} c_{n} \cdot x_{n} & \\
&\mathrm{subject~to} &\displaystyle \sum_{n=1}^{N} a_{n,m} \cdot x_{n} \leq b_{m} & (m = 1, \dots, M) \\
& & x_n \in \{ 0,1 \} &  (n = 1, \dots, N)
\end{array}


式中の  c_n とか  a_{n,m} とか  b_m は定数. 求める変数  x_n が整数(今回は0か1)なのと,目的関数と制約式がともに変数  x_n に対して線形なことに注意する. maximizeはminimizeでもいいし,不等号の向きは逆でもいいし,等式制約があってもいい. 連続変数も含む場合は混合整数線形計画問題(MILP: Mixed-Integer Linear Programming problem)と呼ぶ.

何か解きたい最適化問題があったとして,とにかく上記の整数線形計画問題として表現できれば, CPLEXなどのつよつよ数理計画ソルバにぶん投げることでポンっと最適解を得ることができる.

具体例:ナップサック問題

我々は小学生なので,遠足に持っていくおやつを決めなければならない. おやつ候補は全部で  N 個あり,各おやつ  n \in \{ 1, \dots, N \} は満足度  s_n > 0 と値段  p_n > 0 を持っている. 我々は貪欲な小学生なので,せっかくなら満足度の総和が最大になるようなおやつを選別して遠足に持参したい. しかし,我々は小学生なので,選択したおやつの総計は,先生が決めた予算である  B 円以下に収めなければならない. どのようにおやつを選べば,予算内で満足度を最大化できるだろうか...

簡単のため,各おやつを一個ずつしか選べないとすると, 予算内で満足度を最大化するようなおやつの(部分)集合  S \subseteq \{ 1, \dots, N \} を求める問題になる:


\begin{array}{cll}
&\mathrm{maximize} &\displaystyle \sum_{n \in S} s_{n} & \\
&\mathrm{subject~to} &\displaystyle \sum_{n \in S} p_{n} \leq B & \\
& & S \subseteq \{ 1, \dots, N \} &
\end{array}


上記の最適化問題を,等価な整数線形計画問題として書き直してみる. 変数  x_n \in \{ 0,1 \} が「おやつ  n を選んだかどうか( n \in S)」を表す変数だと思うと, 満足度の総和は  \sum_{n=1}^{N} s_n \cdot x_n ,値段の総計は  \sum_{n=1}^{N} p_n \cdot x_n というように表現できる. そんな感じで,以下のように定式化できる:


\begin{array}{cll}
&\mathrm{maximize} &\displaystyle \sum_{n=1}^{N} s_{n} \cdot x_{n} & \\
&\mathrm{subject~to} &\displaystyle \sum_{n=1}^{N} p_{n} \cdot x_{n} \leq B & \\
& & x_n \in \{ 0,1 \} &  (n = 1, \dots, N)
\end{array}


やったぁ.簡単ですね. 複数個選びたかったら変数  x_n の値域を自然数全体とかに拡張してあげればよいはず.

ILPでモデリングする醍醐味

上記のILP問題を解けば,めでたく最適解が,つまり,予算内で満足度を最大化するおやつ集合が得られる.

しかし,我々は小学生なので,「うまい棒とビッグカツは役割が被るのでどっちか片方でいい」とか 「蒲焼さん太郎を選ぶなら酢だこさん太郎も選ぶだろ」とか「バナナはおやつにry」とか文句を言う可能性がある.

そういった制約も,うまいことモデリングして線形制約式として表現できれば,結局は整数線形計画問題として扱うことができて, やっぱりCPLEXに祈りを捧げるだけで最適解が得られるということになる. よかったね.

この「うまいことモデリングして線形制約式として表現」という部分について, 頻繁に使われるような典型テクと思しきものを,いちいち調べずに済むようまとめておこう, というのが本記事の主旨である(前置きの御託がなげぇ).

制約でよく使うやつ

論理関係

二値変数  x_n \in \{ 0,1 \} について,  x_n = 0 が偽, x_n = 1 が真を表すようなブール変数だと思うことにして,変数間の論理関係についての制約を線形不等式で表現する. モデリングに込めたい大抵のお気持ちはこれで表現できる(気がする).

例えば,「うまい棒とビッグカツは役割が被るのでどっちか片方でいい」とか「蒲焼さん太郎を選ぶなら酢だこさん太郎も選ぶだろ」とかは,それぞれ以下の制約で書ける:


x_\text{うまい棒} + x_\text{ビッグカツ} \leq 1 \\
x_\text{蒲焼さん太郎} \leq x_\text{酢だこさん太郎}


うまい棒とビッグカツの両方を選ぶと  x_\text{うまい棒} + x_\text{ビッグカツ} = 2 となり制約に反するので,同時に選ぶことはできない. また,蒲焼さん太郎を選ぶ( x_\text{蒲焼さん太郎}=1)と制約式は  1 \leq x_\text{酢だこさん太郎} になって,酢だこさん太郎も同時に選ぶことになる.

以下では,二値変数  x_n \in \{ 0,1 \} に対する論理演算をいくつか考える. 演算結果を表す補助変数を  z \in \{ 0,1 \} として,線形不等式で論理演算を表現すると,次のようになる:

AND

2つの変数  x_i, x_j \in \{ 0,1 \} に対するAND演算  x_i \land x_j は,


x_i \land x_j = z 
\iff
\begin{cases}
x_i + x_j - z \leq 1 \\
-x_i + z \leq 0 \\
-x_j + z \leq 0 
\end{cases}
\iff
0 \leq x_1 + x_2 - 2 \cdot z \leq 1


これを2つ以上の場合に一般化すると,


x_1 \land \dots \land  x_N = z 
\iff
0 \leq \sum_{n=1}^{N} x_n - N \cdot z \leq N - 1


OR

2つの変数  x_i, x_j \in \{ 0,1 \} に対するOR演算  x_i \lor x_j は,


x_i \lor x_j = z 
\iff
\begin{cases}
x_i + x_j - z \geq 0 \\
x_i - z \leq 0 \\
x_j - z \leq 0 
\end{cases}
\iff
0 \leq -x_1 - x_2 + 2 \cdot z \leq 1


これを2つ以上の場合に一般化すると,


x_1 \lor \dots \lor  x_N = z 
\iff
0 \leq N \cdot z - \sum_{n=1}^{N} x_n \leq N - 1


XOR

2つの変数  x_i, x_j \in \{ 0,1 \} に対するXOR演算  x_i \oplus x_j は,


x_i \oplus x_j = z 
\iff
\begin{cases}
x_i + x_j - z \geq 0 \\
x_i - x_j - z \leq 0 \\
-x_i + x_j - z \leq 0 \\
x_i + x_j + z \leq 2
\end{cases}


これもANDやORと同様に一本の不等式で書けるかはよくわからない...

離接制約

変数  x_n \in \mathbb{R} に対して,次のような制約を表現したいとする:


\sum_{n=1}^{N} a_{n,1} \cdot x_n \leq b_1 
\lor
\sum_{n=1}^{N} a_{n,2} \cdot x_n \leq b_2


こういうときは大抵,Big-M制約と呼ばれる制約を使って表現する. 補助変数として二値変数  z \in \{ 0,1 \} を導入すると,次のように表現できる:


\sum_{n=1}^{N} a_{n,1} \cdot x_n - b_1 \leq M_1 \cdot (1-z) \\
\sum_{n=1}^{N} a_{n,2} \cdot x_n - b_2 \leq M_2 \cdot z


ここで  M_1, M_2 は条件  M_m \geq \max_{ x_n } \sum_{n=1}^{N} a_{n,m} \cdot x_n - b_m を満たす定数. 定数  M_1, M_2 が条件を満たしていれば,必ずどちらか片方の制約は変数  x_n に関わらず成立するようになる(冗長というらしい).

クソデカ定数  M はなるべく小さい値にしてあげないとソルバが不安定になる(解くのに時間がかかる)ので注意が必要. というか可能ならばBig-M制約はなるべく使わない方がいいらしい...

絶対値

変数  x_n \in \mathbb{R} に対して,次の制約を満たす変数  y \geq 0 を導入したいとする:


y = | \sum_{n=1}^{N} c_{n} \cdot x_n |


これもやっぱり,補助変数として二値変数  z \in \{ 0,1 \} を導入し,  M を条件  M \geq \max_{ x_n } | \sum_{n=1}^{N} c_n \cdot x_n | を満たす定数として,Big-M制約で表現できる:


y^{+} - y^{-} = \sum_{n=1}^{N} c_{n} \cdot x_n \\
y^{+} + y^{-} = y \\
y^{+} \leq M \cdot z \\
y^{-} \leq M \cdot (1-z)


ここで  y^+, y^- \geq 0 は非負の補助変数で,それぞれ  \sum_{n=1}^{N} c_n \cdot x_n の正の部分と負の部分を表している. 補助変数  z は,絶対値の中身が正なら  z=1 になり,負なら  z=0 になる.

絶対値が入っていても,単に不等式制約  | \sum_{n=1}^{N} a_{n,m} \cdot x_n | \leq b_m を表現したいだけなら,クソデカ定数を使わなくても事足りる:


| \sum_{n=1}^{N} a_{n,m} \cdot x_n | \leq b_m
\iff
-b_m \leq \sum_{n=1}^{N} a_{n,m} \cdot x_n \leq b_m


これはまぁ,絶対値の定義を考えると当たり前だった...

目的関数のためによく使うやつ

max-min型目的関数

変数  x_n \in \mathbb{R} に対して,次のような目的関数を最大化したいとする:


\begin{array}{cl}
&\mathrm{maximize} & \min \{ c_1 \cdot x_1, \dots, c_N \cdot x_N \}
\end{array}


ぱっと見た感じ非線形っぽいけど,補助変数  y \in \mathbb{R} を導入して線形不等式で書き直せる:


\begin{array}{cll}
&\mathrm{maximize} &y & \\
&\mathrm{subject~to} & y \leq c_n \cdot x_{n} & (n = 1, \dots, N)
\end{array}


制約式は任意の  n \in \{ 1, \dots, N \} に対して  y \leq c_n \cdot x_n となることを意味していて, 言い換えると変数  y の上限は  c_1 \cdot x_1, \dots, c_N \cdot x_N の最小値になるよ〜と解釈できる. 変数  y は目的関数として最大化されるので,結果的に上限の値,つまり  c_1 \cdot x_1, \dots, c_N \cdot x_N の最小値に張り付いてくれる.

絶対値

変数  x_n \in \mathbb{R} に対して,次のような目的関数を最小化したいとする:


\begin{array}{cl}
&\mathrm{minimize} & \displaystyle \sum_{n=1}^{N} |c_n \cdot x_n|
\end{array}


誤差項みたいなやつの最小化とか, \ell_1-ノルムとかを使いたいと思うとこの形は避けられない(気がする).

絶対値が  |c_n \cdot x_n| = \max \{ c_n \cdot x_n, -c_n \cdot x_n  \} と表現できることを思い出すと, やっぱり補助変数  y_n \geq 0 を導入して線形不等式で書き直せる:


\begin{array}{cll}
&\mathrm{minimize} & \displaystyle \sum_{n=1}^{N} y_n & \\
&\mathrm{subject~to} & y_n \geq c_n \cdot x_{n} & (n = 1, \dots, N) \\
& & y_n \geq - c_n \cdot x_{n}  & (n = 1, \dots, N)
\end{array}


 y_n は最小化されるので,制約式で  c_n \cdot x_n - c_n \cdot x_n のどちらか大きい方の値,つまり  \max \{ c_n \cdot x_n, -c_n \cdot x_n \}  = |c_n \cdot x_n| に張り付くことになる.

いかがでしたか?????

目的関数はどっちかというと線形計画法のテクニックじゃったな...