アダマール問題の最適化バージョン


11

まず、いくつかの定義。

A アダマール行列は、そのエントリが+1または-1と行互いに直交しているのいずれかである正方行列です。アダマール推測は順序4Kのアダマール行列は、すべての正の整数kに対して存在することを提案しています。

巡回行列は、各行ベクトルは、前の行ベクトルに対して右に一つの要素を回転させる行列の特別な種類です。つまり、行列は最初の行で定義されます。

4行4列の行列を除き、循環アダマール行列ないことが知られています。

m行、n> = m列の行列は、一部の循環行列の最初のm行である場合、部分循環です。

タスク

2から始まる各偶数整数nに対して、すべての行が相互に直交するという性質を持つ、+-1エントリとn列の最大部分循環行列のサイズを出力します。

スコア

あなたのスコアは最高nなので、k <= n他の誰もあなたよりも高い正解を投稿していません。明らかに、すべての最適な回答があれば、n投稿した最高のスコアが得られます。しかし、たとえあなたの答えが最適でなくても、他の誰もそれを打つことができないなら、あなたはまだスコアを得ることができます。

言語とライブラリ

使用可能な任意の言語とライブラリを使用できます。可能であれば、コードを実行できるとよいので、可能な限りLinuxでコードを実行/コンパイルする方法の完全な説明を含めてください。

主要なエントリー

  • 64 Pythonの Mitch Schwartzによる

回答:


7

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ます。n4で割り切れない場合は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 + 2m = 1最適なのは、間で符号反転m >= 2を正確に行う必要があるためであり、奇数の符号反転が意味するためです。n/2{(a1,a2), (a2,a3), ... (a{n-1},an), (an,a1)}a1 = -a1
  • 二列のドット積i及びj巡回行列では、によって決定されますabs(i-j)。たとえば、row1 . row2 = 0thenの場合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厳密な上限である推測は自然ですが、それがどのように証明されるかはわかりません。mn > 4


16行32列のソリューションがないことは非常に興味深いです。理由はありますか?

@Lembikもしアイデアがあれば、答えにそれを書いていただろう。
ミッチシュワルツ
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.