締め切り駆動開発

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

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

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

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

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

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

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

maximizen=1Ncnxnsubject ton=1Nan,mxnbm(m=1,,M)xn{0,1}(n=1,,N)


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

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

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

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

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

maximizenSsnsubject tonSpnBS{1,,N}


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

maximizen=1Nsnxnsubject ton=1NpnxnBxn{0,1}(n=1,,N)


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

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

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

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

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

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

制約でよく使うやつ

論理関係

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

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

xうまい棒+xビッグカツ1x蒲焼さん太郎x酢だこさん太郎


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

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

AND

2つの変数 xi,xj{0,1} に対するAND演算 xixj は,

xixj=z{xi+xjz1xi+z0xj+z00x1+x22z1


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

x1xN=z0n=1NxnNzN1


OR

2つの変数 xi,xj{0,1} に対するOR演算 xixj は,

xixj=z{xi+xjz0xiz0xjz00x1x2+2z1


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

x1xN=z0Nzn=1NxnN1


XOR

2つの変数 xi,xj{0,1} に対するXOR演算 xixj は,

xixj=z{xi+xjz0xixjz0xi+xjz0xi+xj+z2


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

離接制約

変数 xnR に対して,次のような制約を表現したいとする:

n=1Nan,1xnb1n=1Nan,2xnb2


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

n=1Nan,1xnb1M1(1z)n=1Nan,2xnb2M2z


ここで M1,M2 は条件 Mmmaxxnn=1Nan,mxnbm を満たす定数. 定数 M1,M2 が条件を満たしていれば,必ずどちらか片方の制約は変数 xn に関わらず成立するようになる(冗長というらしい).

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

絶対値

変数 xnR に対して,次の制約を満たす変数 y0 を導入したいとする:

y=|n=1Ncnxn|


これもやっぱり,補助変数として二値変数 z{0,1} を導入し, M を条件 Mmaxxn|n=1Ncnxn| を満たす定数として,Big-M制約で表現できる:

y+y=n=1Ncnxny++y=yy+MzyM(1z)


ここで y+,y0 は非負の補助変数で,それぞれ n=1Ncnxn の正の部分と負の部分を表している. 補助変数 z は,絶対値の中身が正なら z=1 になり,負なら z=0 になる.

絶対値が入っていても,単に不等式制約 |n=1Nan,mxn|bm を表現したいだけなら,クソデカ定数を使わなくても事足りる:

|n=1Nan,mxn|bmbmn=1Nan,mxnbm


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

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

max-min型目的関数

変数 xnR に対して,次のような目的関数を最大化したいとする:

maximizemin{c1x1,,cNxN}


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

maximizeysubject toycnxn(n=1,,N)


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

絶対値

変数 xnR に対して,次のような目的関数を最小化したいとする:

minimizen=1N|cnxn|


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

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

minimizen=1Nynsubject toyncnxn(n=1,,N)yncnxn(n=1,,N)


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

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

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