最短の最長は35。
ORも使っていいなら31。
関数 最短長 式の種類数(左右を入れ替えただけのものは同一とみなす)
式の例
関数の個数(=2^(2^変数の個数))をNとして、式はO(N^2)の適当な探索で復元した。
各関数における最短を求めるだけなら、式の長さの上限をLとして、O(L^2 N log N)で求められる。(変数が5個でも数日で実行が終わる!すごい!)
dp[0~N-1]を埋めることが目標。
0,1,a,b,c,dにあたる場所を1で埋めておく。
for l = 1~L:
s[] = dp[i]がlである場所に1、そうでない場所に0を書いた長さNの配列
NOTの更新(a[i]==1ならdp[i^(N-1)]にl+1でminをとる)
for r = 1~l:
t[] = dp[i]がrである場所に1、そうでない場所に0を書いた長さNの配列
ANDの更新(後述)
XORの更新(後述)Lが分かっていない場合は更新がなくなるまで繰り返すとよい。
MをN=2^Mとなるような数(要は桁数)として、ANDの更新は以下の通り。
高速ゼータ変換をして掛けて高速メビウス変換です。
for i = 0~M-1:
for j = 0~N-1:
if (j>>i&1) == 0:
s[j] += s[j|1<<i]
t[j] += t[j|1<<i]
d = 長さNの配列
for i = 0~N-1:
d[i] = s[i]*t[i]
for i = 0~M-1:
for j = 0~N-1:
if (j>>i&1) == 0:
d[j] -= d[j|1<<i]
for i = 0~N-1:
if d[i] > 0:
dp[i] = min(dp[i], l+r+3)次にXOR。(ANDでs,tに破壊的な操作をしましたが元に戻してからやります)
高速アダマール変換をして掛けて逆変換です。さっきと変換方法が変わっただけ。
アダマール変換はM次元でmod2な(離散)FFTです。
ada()はアダマール変換、dad()はその逆変換だと思って読んで下さい(それぞれのコードは別ファイルとしてつけています)
ada(s)
ada(t)
d = 長さNの配列
for i = 0~N-1:
d[i] = s[i]*t[i]
dad(d)
for i = 0~N-1:
if d[i] > 0:
dp[i] = min(dp[i], l+r+3)