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:]
Подход заключается в простом рандомизированном поиске с восхождением на гору, используя преимущества схемы, замеченной для маленьких n. Шаблон заключается в том, что для оптимальности mвторая половина первого ряда часто имеет небольшое расстояние редактирования от (побитового) отрицания первой половины. Результаты для приведенного выше кода хороши для малого, nно начинают ухудшаться вскоре после того, как грубая сила становится невозможной; Я был бы рад видеть лучший подход.
Некоторые наблюдения:
- Когда
nнечетно, m = 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тогда row4 . row5 = 0. Это потому, что пары элементов для точечного произведения одинаковы, только что повернуты.
- Следовательно, для проверки взаимной ортогональности нам нужно только проверить последовательные строки по первому ряду.
- Если мы представим строку в виде двоичной строки с
0вместо -1, мы можем проверить ортогональность двух строк, взяв побитовый xor и сравнив попконт с n/2.
- Мы можем произвольно исправить первые два элемента первой строки, потому что (1) отрицание матрицы не влияет на то, равны ли произведения точек нулю, и (2) мы знаем, что должно быть как минимум два соседних элемента с одинаковым знаком и два смежные элементы с отличающимся знаком, поэтому мы можем вращать, чтобы разместить желаемую пару в начале.
- Решение
(n0, m0)автоматически даст решения (k * n0, m0)для произвольного k > 1, путем (многократного) объединения первой строки с самим собой. Следствием является то, что мы можем легко получить m = 4для любого nделится на 4.
Естественно предположить, что n/2это жесткая верхняя граница, mкогда n > 4, но я не знаю, как это будет доказано.