Una versione di ottimizzazione del problema Hadamard


11

Innanzitutto, alcune definizioni.

Una matrice Hadamard è una matrice quadrata le cui voci sono +1 o −1 e le cui file sono reciprocamente ortogonali. La congettura di Hadamard propone che esiste una matrice Hadamard di ordine 4k per ogni intero positivo k.

Una matrice circolante è un tipo speciale di matrice in cui ciascun vettore di riga viene ruotato di un elemento a destra rispetto al vettore di riga precedente. Cioè la matrice è definita dalla sua prima riga.

È noto che, ad eccezione delle matrici 4 per 4, non vi sono matrici Hadamard circolanti .

Una matrice con m righe e n> = m colonne è circolante parziale , se si tratta delle prime m righe di una matrice circolante.

L'obiettivo

Per ogni intero pari n che inizia da 2, genera la dimensione della matrice circolante parziale più grande con + -1 voci e n colonne che hanno la proprietà che tutte le sue righe sono reciprocamente ortogonali.

Punto

Il tuo punteggio è il più alto in modo ntale che per tutti k <= n, nessun altro ha pubblicato una risposta corretta più alta di te. Chiaramente se hai tutte le risposte ottimali otterrai il punteggio per il punteggio più alto nche pubblichi . Tuttavia, anche se la tua risposta non è ottimale, puoi comunque ottenere il punteggio se nessun altro può batterlo.

Lingue e biblioteche

Puoi utilizzare qualsiasi lingua e libreria disponibili. Laddove possibile, sarebbe opportuno poter eseguire il codice, quindi includere una spiegazione completa su come eseguire / compilare il codice in Linux, se possibile.

Voci principali

  • 64 di Mitch Schwartz in Python

Risposte:


7

Python 2

Tabella fino a n = 64, verificata ottimale con forza bruta fino a 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

dove 0rappresenta -1. Se nnon è divisibile per 4, allora m = 1è ottimale. Generato utilizzando questo codice (o piccole varianti di esso) ma con più prove per livelli superiori 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:]

L'approccio è una semplice ricerca randomizzata con l'arrampicata in collina, sfruttando uno schema notato per i piccoli n. Il modello è che, per la migliore m, la seconda metà della prima riga ha spesso una piccola distanza di modifica dalla negazione (bit a bit) della prima metà. I risultati per il codice sopra sono buoni per i piccoli nma iniziano a deteriorarsi non molto tempo dopo che la forza bruta diventa irrealizzabile; Sarei felice di vedere un approccio migliore.

Alcune osservazioni:

  • Quando nè dispari, m = 1è ottimale perché un numero dispari di quelli negativi e quelli non possono sommarsi a zero. (Ortogonale significa che il prodotto punto è zero.)
  • Quando n = 4k + 2, m = 1è ottimale perché per m >= 2avere bisogno di avere esattamente n/2inversioni di segno tra {(a1,a2), (a2,a3), ... (a{n-1},an), (an,a1)}, e un numero dispari di inversioni di segni implicherebbe a1 = -a1.
  • Il prodotto punto di due file ie jin una matrice circolante è determinato da abs(i-j). Ad esempio, se row1 . row2 = 0allora row4 . row5 = 0. Questo perché le coppie di elementi per il prodotto punto sono le stesse, appena ruotate.
  • Di conseguenza, per verificare l'ortogonalità reciproca, dobbiamo solo verificare le righe successive rispetto alla prima riga.
  • Se rappresentiamo una riga come una stringa binaria con 0al posto di -1, possiamo verificare l'ortogonalità di due righe prendendo xor bit a bit e confrontando il popcount con n/2.
  • Possiamo correggere i primi due elementi della prima riga in modo arbitrario, perché (1) Negare una matrice non influisce sul fatto che i prodotti punto siano uguali a zero e (2) Sappiamo che devono esserci almeno due elementi adiacenti con lo stesso segno e due elementi adiacenti con segno diverso, in modo da poter ruotare per posizionare la coppia desiderata all'inizio.
  • Una soluzione (n0, m0)fornirà automaticamente soluzioni (k * n0, m0)arbitrarie k > 1, concatenando (ripetutamente) la prima riga a se stessa. Una conseguenza è che possiamo facilmente ottenere m = 4per qualsiasi ndivisibile per 4.

È naturale congetturare che n/2è un limite superiore stretto per mquando n > 4, ma non so come sarebbe dimostrato.


È molto interessante che non ci sia soluzione con 16 righe e 32 colonne. Hai idea del perché?

@Lembik Se avessi avuto un'idea, l'avrei scritto nella risposta.
Mitch Schwartz,
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.