Una versión de optimización del problema de Hadamard

11

Primero, algunas definiciones.

Una matriz de Hadamard es una matriz cuadrada cuyas entradas son +1 o −1 y cuyas filas son mutuamente ortogonales. La conjetura de Hadamard propone que existe una matriz de Hadamard de orden 4k para cada entero positivo k.

Una matriz circulante es un tipo especial de matriz donde cada vector de fila gira un elemento a la derecha en relación con el vector de fila anterior. Es decir, la matriz se define por su primera fila.

Se sabe que, a excepción de las matrices de 4 por 4, no hay matrices de Hadamard circulantes .

Una matriz con m filas yn> = m columnas es de circulación parcial , si son las primeras m filas de alguna matriz circulante.

La tarea

Para cada número entero par n que comience en 2, genere el tamaño de la matriz circulante parcial más grande con + -1 entradas yn columnas que tengan la propiedad de que todas sus filas son mutuamente ortogonales.

Puntuación

Su puntaje es el más alto, de nmodo que, para todos k <= n, nadie más ha publicado una respuesta correcta más alta que usted. Claramente, si tiene todas las respuestas óptimas, obtendrá la puntuación más alta nque publique. Sin embargo, incluso si su respuesta no es la óptima, aún puede obtener el puntaje si nadie más puede superarlo.

Idiomas y bibliotecas

Puede usar cualquier idioma y bibliotecas disponibles que desee. Siempre que sea posible, sería bueno poder ejecutar su código, así que incluya una explicación completa de cómo ejecutar / compilar su código en Linux, si es posible.

Entradas principales

  • 64 por Mitch Schwartz en Python
falla
fuente

Respuestas:

7

Python 2

Tabla hasta n = 64, verificado óptimo con fuerza bruta hasta 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

donde 0representa -1. Si nno es divisible por 4, entonces m = 1es óptimo. Generado usando este código (o pequeñas variaciones del mismo) pero con más pruebas para versiones superiores 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:]

El enfoque es una búsqueda aleatoria simple con escalada, aprovechando un patrón notado para los pequeños n. El patrón es que, para un óptimo m, la segunda mitad de la primera fila a menudo tiene una pequeña distancia de edición desde la negación (bit a bit) de la primera mitad. Los resultados para el código anterior son buenos para los pequeños, npero comienzan a deteriorarse poco después de que la fuerza bruta sea inviable; Estaría feliz de ver un mejor enfoque.

Algunas observaciones

  • Cuando nes impar, m = 1es óptimo porque un número impar de unos y negativos no pueden sumar cero. (Ortogonal significa que el producto escalar es cero).
  • Cuando n = 4k + 2, m = 1es óptima porque a fin de m >= 2que tenemos que tener exactamente n/2firmar reversiones entre {(a1,a2), (a2,a3), ... (a{n-1},an), (an,a1)}, y un número impar de inversiones de signo implicaría a1 = -a1.
  • El producto escalar de dos filas iy jen una matriz circulante está determinado por abs(i-j). Por ejemplo, si row1 . row2 = 0entonces row4 . row5 = 0. Esto se debe a que los pares de elementos para el producto escalar son iguales, simplemente rotados.
  • En consecuencia, para verificar la ortogonalidad mutua, solo necesitamos verificar filas sucesivas contra la primera fila.
  • Si representamos una fila como una cadena binaria 0en lugar de -1, podemos verificar la ortogonalidad de dos filas tomando bit xor y comparando el popcount con n/2.
  • Podemos arreglar los dos primeros elementos de la primera fila de manera arbitraria, porque (1) Negar una matriz no afecta si los productos de punto son iguales a cero, y (2) Sabemos que debe haber al menos dos elementos adyacentes con el mismo signo y dos elementos adyacentes con signo diferente, por lo que podemos rotar para colocar el par deseado al principio.
  • Una solución (n0, m0)dará automáticamente soluciones (k * n0, m0)arbitrarias k > 1, concatenando (repetidamente) la primera fila consigo misma. Una consecuencia es que podemos obtener fácilmente m = 4cualquier ndivisible por 4.

Es natural conjeturar que n/2es un límite superior ajustado para mcuándo n > 4, pero no sé cómo se probaría eso.

Mitch Schwartz
fuente
Es muy interesante que no haya solución con 16 filas y 32 columnas. ¿Tienes alguna idea de por qué?
@Lembik Si tuviera una idea, la habría escrito en la respuesta.
Mitch Schwartz