Python 2
以下の表、最大n = 64
で総当たりで最適な検証済みn = 32
:
4 4 0001
8 4 00010001
12 6 000001010011
16 8 0000010011101011
20 10 00010001011110011010
24 12 000101001000111110110111
28 14 0001011000010011101011111011
32 14 00001101000111011101101011110010
36 18 001000101001000111110011010110111000
40 20 0010101110001101101111110100011100100100
44 18 00010000011100100011110110110101011101101111
48 24 001011011001010111111001110000100110101000000110
52 26 0011010111000100111011011111001010001110100001001000
56 28 00100111111101010110001100001101100000001010100111001011
60 30 000001101101100011100101011101111110010010111100011010100010
64 32 0001100011110101111111010010011011100111000010101000001011011001
はを0
表し-1
ます。n
4で割り切れない場合はm = 1
最適です。このコード(またはそれの小さなバリエーション)を使用して生成されますが、より高度なトライアルが必要ですn
:
from random import *
seed(10)
trials=10000
def calcm(x,n):
m=1
y=x
while 1:
y=((y&1)<<(n-1))|(y>>1)
if bin(x^y).count('1')!=n/2:
return m
m+=1
def hillclimb(x,n,ns):
bestm=calcm(x,n)
while 1:
cands=[]
for pos in ns:
xx=x^(1<<pos)
m=calcm(xx,n)
if m>bestm:
bestm=m
cands=[xx]
elif cands and m==bestm:
cands+=[xx]
if not cands:
break
x=choice(cands)
return x,bestm
def approx(n):
if n<10: return brute(n)
bestm=1
bestx=0
for trial in xrange(1,trials+1):
if not trial&16383:
print bestm,bin((1<<n)|bestx)[3:]
if not trial&1:
x=randint(0,(1<<(n/2-2))-1)
x=(x<<(n/2)) | (((1<<(n/2))-1)^x)
ns=range(n/2-2)
if not trial&7:
adj=randint(1,5)
x^=((1<<adj)-1)<<randint(0,n/2-adj)
else:
x=randint(0,(1<<(n-2))-1)
ns=range(n-2)
x,m=hillclimb(x,n,ns)
if m>bestm:
bestm=m
bestx=x
return bestm,bestx
def brute(n):
bestm=1
bestx=0
for x in xrange(1<<(n-2)):
m=calcm(x,n)
if m>bestm:
bestm=m
bestx=x
return bestm,bestx
for n in xrange(4,101,4):
m,x=approx(n)
print n,m,bin((1<<n)|x)[3:]
このアプローチは、山登りを使用した単純なランダム検索であり、smallで認識されるパターンを利用していますn
。パターンは、最適化のためm
に、最初の行の後半の多くの場合、前半の(ビットごとの)否定からの編集距離が小さいということです。上記のコードの結果は小さなものには適していますがn
、ブルートフォースが実行不可能になってから間もなく悪化し始めます。もっと良いアプローチを見つけてうれしいです。
いくつかの観察:
n
が奇数の場合m = 1
、奇数の1と負の1は合計できないため最適です。(直交とは、内積がゼロであることを意味します。)
- の場合
n = 4k + 2
、m = 1
最適なのは、間で符号反転m >= 2
を正確に行う必要があるためであり、奇数の符号反転が意味するためです。n/2
{(a1,a2), (a2,a3), ... (a{n-1},an), (an,a1)}
a1 = -a1
- 二列のドット積
i
及びj
巡回行列では、によって決定されますabs(i-j)
。たとえば、row1 . row2 = 0
thenの場合row4 . row5 = 0
。これは、内積の要素のペアが同じで、回転しただけだからです。
- したがって、相互の直交性をチェックするには、最初の行に対して連続する行をチェックするだけです。
- 行をの
0
代わりにバイナリ文字列として表す場合-1
、ビット単位のxorを取得し、popcountをと比較することにより、2行の直交性を確認できn/2
ます。
- (1)行列の否定は内積がゼロに等しいかどうかには影響しないため、(2)同じ符号と2つの隣接する要素が少なくとも2つ必要であることがわかっているため異なる符号を持つ隣接する要素なので、回転して目的のペアを先頭に配置できます。
- ソリューション
(n0, m0)
は、(繰り返し)最初の行をそれ自体に連結することにより(k * n0, m0)
、任意のソリューションを自動的に提供k > 1
します。結果として、4で割り切れるm = 4
すべての値を簡単に取得できますn
。
whenのn/2
厳密な上限である推測は自然ですが、それがどのように証明されるかはわかりません。m
n > 4