Riempi le righe, le colonne e le diagonali di una griglia NxN da 1 a N


26

Compito

Dato input N, genera e genera una griglia NxN in cui ogni riga, colonna e le due diagonali contengono i numeri da 1 a N(o da 0 a N−1 se è più semplice).

Ingresso

L'input è un numero intero positivo N. Rappresenta il numero di colonne e righe nella griglia. Per questo problema, puoi presumere che Navrà dimensioni ragionevoli 4 ≤ N ≤ 8o ( 1 ≤ N ≤ 8se scegli il bonus di seguito).

Produzione

L'output sarà la griglia N× N. Nella griglia, ogni riga contiene solo i numeri da 1 a N, ogni colonna contiene solo i numeri da 1 a N, e le due diagonali di lunghezza N(quella da (0,0)a (N-1,N-1)e quella da (0,N-1)a (N-1, 0)) contengono solo i numeri da 1 a N. È possibile utilizzare i numeri da 0 a N−1. Per ognuna N, ci sono molte soluzioni possibili, devi solo stampare la prima che trovi. Non è necessario stampare spazi tra i numeri.

vincoli

Il tuo codice dovrebbe essere in grado di produrre ripetutamente risultati per N >= 7. Cioè, se sei in grado di eseguire effettivamente e ottenere una soluzione N = 7dal tuo codice ogni volta, sei a posto. In termini di limite assoluto, il tuo codice dovrebbe essere in grado di risolversi N = 7in meno di 10 minuti ogni volta che lo esegui (ovvero, se dipendi da numeri casuali, nel caso peggiore, il tuo codice dovrebbe comunque finire in meno di 10 minuti per N = 7) .

Esempi

  • Ingresso: 4

    Un possibile output:

    1 2 3 4
    3 4 1 2
    4 3 2 1
    2 1 4 3
    
  • Ingresso: 5

    Un possibile output:

    1 2 3 4 5
    5 3 1 2 4
    2 5 4 3 1
    4 1 2 5 3
    3 4 5 1 2
    
  • Ingresso: 8

    Un possibile output:

    1 2 3 4 5 6 7 8
    2 3 1 5 4 8 6 7
    4 1 2 3 7 5 8 6
    6 4 7 8 1 2 3 5
    7 5 8 2 6 3 4 1
    5 8 4 6 2 7 1 3
    8 7 6 1 3 4 5 2
    3 6 5 7 8 1 2 4
    

punteggio

Questo è , quindi vince il codice più breve in byte, con un'eccezione. Per gli input N = 2, 3non ci sono soluzioni valide. Se il tuo codice è in grado di gestirlo (esegui fino al completamento senza generare nulla per questi casi, o emettendo una stringa vuota), e gestisce ancora N = 1(output 1per esso), risparmia il 20% sul conteggio dei byte.


1
Correlato , ma una griglia del genere non funzionerà qui a causa del requisito delle diagonali.
xnor

Mi piace questa sfida ma non riesco a capire un algoritmo per valori pari a N. Questo codice JavaScript funziona N = 1, 5 or 7anche se aiuta qualcuno:for(o="",y=N;y--;o+="\n")for(x=N;x--;)o+=(((N-y)*2+x)%N)+1
user81655

Molto correlato, possibile duplicato: codegolf.stackexchange.com/q/47846/15599
Level River St

@steveverrill Non era un codice golf.
randomra,

1
Forse dovresti essere più esplicito sul N = 1caso: le risposte che mirano al bonus dovrebbero tornare 1, non la stringa vuota.
Lynn il

Risposte:


3

Python 3, 275 260 byte * 0,8 = 220 208 byte

Approccio ricorsivo / backtracking. Rè la funzione ricorsiva, lè la colonna , è il roW, wè la Kvoce successiva.

Ho scelto di inserirlo in un array 1d e di stamparlo alla fine per semplificare gli indici.

r=range
n=(int)(input())
def R(A,I):
 l=I%n;w=I//n
 if I==n*n:[print(A[i*n:i*n+n])for i in r(n)];exit()
 for K in r(n):
  if all([all([A[i*n+l]!=K,w!=l or A[i+n*i]!=K,w!=n-1-l or A[n*i+n-i-1]!=K])for i in r(w)]+[A[w*n+i]!=K for i in r(l)]):R(A+[K],I+1)
R([],0)

Versione non golfata:

def Recurse(A,I,n):
 column=I%n
 row=I//n
 if I==n*n:
     [print(*A[i*n:i*n+n])for i in range(n)] # output
     exit() #end recursion
 for K in range(n):
    # try every possibility. Test if they satisfy the constraints:
    # if so, move the index on. If none of them do, we'll return None.
    # if a child returns None, we'll move onto the next potential child:
    # if all of them fail it will backtrack to the next level.
    if all([
        all([
            A[i*n+column]!=K, # column constraint
            row!=column or A[i+n*i]!=K, # diagonal constraint
            row!=n-1-column or A[n*i+n-i-1]!=K # antidiagonal constraint
            ]) for i in range(row)
        ]+[
            A[row*n+i]!=K for i in range(column) # row constraint
            ]):
        Recurse(A+[K],I+1,n)

Recurse([],0,(int)(input()))

22

Funciton , non competitivo

AGGIORNARE! Massiccio miglioramento delle prestazioni! n = 7 ora completa in meno di 10 minuti! Vedi spiegazione in fondo!

È stato molto divertente scrivere. Questo è un risolutore di forza bruta per questo problema scritto a Funciton. Alcuni factoidi:

  • Accetta un numero intero su STDIN. Qualsiasi spazio bianco estraneo lo interrompe, inclusa una nuova riga dopo l'intero.
  • Usa i numeri da 0 a n - 1 (non da 1 a n ).
  • Riempie la griglia "all'indietro", in modo da ottenere una soluzione in cui legge la riga inferiore 3 2 1 0anziché in quella della riga superiore 0 1 2 3.
  • Emette correttamente 0(l'unica soluzione) per n = 1.
  • Uscita vuota per n = 2 e n = 3.
  • Quando compilato per un exe, richiede circa 8¼ minuti per n = 7 (era circa un'ora prima del miglioramento delle prestazioni). Senza compilare (usando l'interprete) ci vuole circa 1,5 volte di più, quindi vale la pena usare il compilatore.
  • Come traguardo personale, questa è la prima volta che scrivo un intero programma Funciton senza prima scrivere il programma in un linguaggio pseudocodico. L'ho scritto prima in C # vero però.
  • (Tuttavia, questa non è la prima volta che ho apportato una modifica per migliorare in modo massiccio le prestazioni di qualcosa a Funciton. La prima volta che l'ho fatto è stata nella funzione fattoriale. Lo scambio dell'ordine degli operandi della moltiplicazione ha fatto una differenza enorme a causa di come funziona l'algoritmo di moltiplicazione . Nel caso fossi curioso.)

Senza ulteriori indugi:

            ┌────────────────────────────────────┐           ┌─────────────────┐
            │                                  ┌─┴─╖ ╓───╖ ┌─┴─╖   ┌──────┐    │
            │                    ┌─────────────┤ · ╟─╢ Ӂ ╟─┤ · ╟───┤      │    │
            │                    │             ╘═╤═╝ ╙─┬─╜ ╘═╤═╝ ┌─┴─╖    │    │
            │                    │               └─────┴─────┘   │ ♯ ║    │    │
            │                  ┌─┴─╖                             ╘═╤═╝    │    │
            │     ┌────────────┤ · ╟───────────────────────────────┴───┐  │    │
          ┌─┴─╖ ┌─┴─╖   ┌────╖ ╘═╤═╝ ┌──────────┐         ┌────────┐ ┌─┴─╖│    │
          │ ♭ ║ │ × ╟───┤ >> ╟───┴───┘        ┌─┴─╖       │ ┌────╖ └─┤ · ╟┴┐   │
          ╘═╤═╝ ╘═╤═╝   ╘══╤═╝          ┌─────┤ · ╟───────┴─┤ << ╟─┐ ╘═╤═╝ │   │
    ┌───────┴─────┘ ┌────╖ │            │     ╘═╤═╝         ╘══╤═╝ │   │   │   │
    │     ┌─────────┤ >> ╟─┘            │       └───────┐      │   │   │   │   │
    │     │         ╘══╤═╝            ┌─┴─╖   ╔═══╗   ┌─┴─╖ ┌┐ │   │ ┌─┴─╖ │   │
    │     │           ┌┴┐     ┌───────┤ ♫ ║ ┌─╢ 0 ║ ┌─┤ · ╟─┤├─┤   ├─┤ Ӝ ║ │   │
    │     │   ╔═══╗   └┬┘     │       ╘═╤═╝ │ ╚═╤═╝ │ ╘═╤═╝ └┘ │   │ ╘═╤═╝ │   │
    │     │   ║ 1 ╟───┬┘    ┌─┴─╖       └───┘ ┌─┴─╖ │   │      │   │   │ ┌─┴─╖ │
    │     │   ╚═══╝ ┌─┴─╖   │ ɓ ╟─────────────┤ ? ╟─┘   │    ┌─┴─╖ │   ├─┤ · ╟─┴─┐
    │     ├─────────┤ · ╟─┐ ╘═╤═╝             ╘═╤═╝   ┌─┴────┤ + ╟─┘   │ ╘═╤═╝   │
  ┌─┴─╖ ┌─┴─╖       ╘═╤═╝ │ ╔═╧═╕ ╔═══╗ ┌───╖ ┌─┴─╖ ┌─┴─╖    ╘═══╝     │   │     │
┌─┤ · ╟─┤ · ╟───┐     └┐  └─╢   ├─╢ 0 ╟─┤ ⌑ ╟─┤ ? ╟─┤ · ╟──────────────┘   │     │
│ ╘═╤═╝ ╘═╤═╝   └───┐ ┌┴┐   ╚═╤═╛ ╚═╤═╝ ╘═══╝ ╘═╤═╝ ╘═╤═╝                  │     │
│   │   ┌─┴─╖ ┌───╖ │ └┬┘   ┌─┴─╖ ┌─┘           │     │                    │     │
│ ┌─┴───┤ · ╟─┤ Җ ╟─┘  └────┤ ? ╟─┴─┐   ┌─────────────┘                    │     │
│ │     ╘═╤═╝ ╘═╤═╝         ╘═╤═╝   │   │╔════╗╔════╗                      │     │
│ │       │  ┌──┴─╖ ┌┐   ┌┐ ┌─┴─╖ ┌─┴─╖ │║ 10 ║║ 32 ║    ┌─────────────────┘     │
│ │       │  │ << ╟─┤├─┬─┤├─┤ · ╟─┤ · ╟─┘╚══╤═╝╚╤═══╝ ┌──┴──┐                    │
│ │       │  ╘══╤═╝ └┘ │ └┘ ╘═╤═╝ ╘═╤═╝     │ ┌─┴─╖ ┌─┴─╖ ┌─┴─╖                  │
│ │     ┌─┴─╖ ┌─┴─╖  ┌─┴─╖  ┌─┴─╖ ╔═╧═╕     └─┤ ? ╟─┤ · ╟─┤ % ║                  │
│ └─────┤ · ╟─┤ · ╟──┤ Ӂ ╟──┤ ɱ ╟─╢   ├───┐   ╘═╤═╝ ╘═╤═╝ ╘═╤═╝                  │
│       ╘═╤═╝ ╘═╤═╝  ╘═╤═╝  ╘═══╝ ╚═╤═╛ ┌─┴─╖ ┌─┴─╖   │     └────────────────────┘
│         └─────┤      │            └───┤ ‼ ╟─┤ ‼ ║   │        ┌──────┐
│               │      │                ╘═══╝ ╘═╤═╝   │        │ ┌────┴────╖
│               │      │                      ┌─┴─╖   │        │ │ str→int ║
│               │      └──────────────────────┤ · ╟───┴─┐      │ ╘════╤════╝
│               │          ┌─────────╖        ╘═╤═╝     │    ╔═╧═╗ ┌──┴──┐
│               └──────────┤ int→str ╟──────────┘       │    ║   ║ │ ┌───┴───┐
│                          ╘═════════╝                  │    ╚═══╝ │ │ ┌───╖ │
└───────────────────────────────────────────────────────┘          │ └─┤ × ╟─┘
           ┌──────────────┐                                  ╔═══╗ │   ╘═╤═╝
╔════╗     │ ╓───╖ ┌───╖  │                              ┌───╢ 0 ║ │   ┌─┴─╖ ╔═══╗
║ −1 ║     └─╢ Ӝ ╟─┤ × ╟──┴──────┐                       │   ╚═╤═╝ └───┤ Ӂ ╟─╢ 0 ║
╚═╤══╝       ╙───╜ ╘═╤═╝         │                       │   ┌─┴─╖     ╘═╤═╝ ╚═══╝
┌─┴──╖ ┌┐ ┌───╖ ┌┐ ┌─┴──╖ ╔════╗ │                       │ ┌─┤   ╟───────┴───────┐
│ << ╟─┤├─┤ ÷ ╟─┤├─┤ << ║ ║ −1 ║ │                       │ │ └─┬─╜ ┌─┐ ┌─────┐   │
╘═╤══╝ └┘ ╘═╤═╝ └┘ ╘═╤══╝ ╚═╤══╝ │                       │ │   └───┴─┘ │   ┌─┴─╖ │
  │         └─┘      └──────┘    │                       │ └───────────┘ ┌─┤ ? ╟─┘
  └──────────────────────────────┘         ╓───╖         └───────────────┘ ╘═╤═╝
                               ┌───────────╢ Җ ╟────────────┐                │
      ┌────────────────────────┴───┐       ╙───╜            │
      │                          ┌─┴────────────────────┐ ┌─┴─╖
    ┌─┴─╖                      ┌─┴─╖                  ┌─┴─┤ · ╟──────────────────┐
    │ ♯ ║ ┌────────────────────┤ · ╟───────┐          │   ╘═╤═╝                  │
    ╘═╤═╝ │                    ╘═╤═╝       │          │     │              ┌───╖ │
┌─────┴───┘    ┌─────────────────┴─┐   ┌───┴───┐    ┌─┴─╖ ┌─┴─╖          ┌─┤ × ╟─┴─┐
│              │                 ┌─┴─╖ │   ┌───┴────┤ · ╟─┤ · ╟──────────┤ ╘═╤═╝   │
│              │ ┌───╖ ┌───╖  ┌──┤ · ╟─┘ ┌─┴─┐      ╘═╤═╝ ╘═╤═╝        ┌─┴─╖ │     │
│         ┌────┴─┤ ♭ ╟─┤ × ╟──┘  ╘═╤═╝   │ ┌─┴─╖ ┌───╖└┐ ┌──┴─╖      ┌─┤ · ╟─┘     │
│         │      ╘═══╝ ╘═╤═╝ ┌───╖ │     │ │ × ╟─┤ Ӝ ╟─┴─┤ ÷% ╟─┐    │ ╘═╤═╝ ┌───╖ │
│   ┌─────┴───┐     ┌────┴───┤ Ӝ ╟─┴─┐   │ ╘═╤═╝ ╘═╤═╝   ╘══╤═╝ │    │   └───┤ Ӝ ╟─┘
│ ┌─┴─╖ ┌───╖ │     │ ┌────╖ ╘═╤═╝   │   └───┘   ┌─┴─╖      │   │    └────┐  ╘═╤═╝
│ │ × ╟─┤ Ӝ ╟─┘     └─┤ << ╟───┘   ┌─┴─╖ ┌───────┤ · ╟───┐  │ ┌─┴─╖ ┌───╖ │    │
│ ╘═╤═╝ ╘═╤═╝         ╘══╤═╝   ┌───┤ + ║ │       ╘═╤═╝   ├──┴─┤ · ╟─┤ × ╟─┘    │
└───┤     └────┐ ╔═══╗ ┌─┴─╖ ┌─┴─╖ ╘═╤═╝ │ ╔═══╗ ┌─┴─╖ ┌─┴─╖  ╘═╤═╝ ╘═╤═╝      │
  ┌─┴─╖ ┌────╖ │ ║ 0 ╟─┤ ? ╟─┤ = ║  ┌┴┐  │ ║ 0 ╟─┤ ? ╟─┤ = ║    │     │ ┌────╖ │
  │ × ╟─┤ << ╟─┘ ╚═══╝ ╘═╤═╝ ╘═╤═╝  └┬┘  │ ╚═══╝ ╘═╤═╝ ╘═╤═╝    │     └─┤ << ╟─┘
  ╘═╤═╝ ╘═╤══╝ ┌┐     ┌┐ │     │     └───┘       ┌─┴─╖   ├──────┘       ╘═╤══╝
    │     └────┤├──┬──┤├─┘     ├─────────────────┤ · ╟───┘                │
    │          └┘┌─┴─╖└┘       │     ┌┐   ┌┐     ╘═╤═╝ ┌┐   ┌┐            │
    └────────────┤ · ╟─────────┘   ┌─┤├─┬─┤├─┐     └───┤├─┬─┤├────────────┘
                 ╘═╤═╝             │ └┘ │ └┘ │         └┘ │ └┘
                   └───────────────┘    │    └────────────┘

Spiegazione della prima versione

La prima versione ha impiegato circa un'ora per risolvere n = 7. Quanto segue spiega principalmente come ha funzionato questa versione lenta. In fondo spiegherò quale modifica ho apportato per farlo arrivare a meno di 10 minuti.

Un'escursione in pezzi

Questo programma ha bisogno di bit. Ha bisogno di molti bit e ne ha bisogno nei posti giusti. I programmatori esperti di Funciton sanno già che se hai bisogno di n bit, puoi usare la formula

2 ^ n-1

che a Funciton può essere espresso come

(1 << n) - 1

Durante l'ottimizzazione delle prestazioni, mi è venuto in mente che posso calcolare lo stesso valore molto più velocemente utilizzando questa formula:

¬ (−1 << n)

Spero che mi perdonerai che non ho aggiornato di conseguenza tutta la grafica delle equazioni in questo post.

Ora, supponiamo che tu non voglia un blocco contiguo di bit; in effetti, vuoi n bit a intervalli regolari ogni k -esimo bit, in questo modo:

                                 LSB
                                  ↓
00000010000001000000100000010000001
                            └──┬──┘
                               k

La formula per questo è abbastanza semplice una volta che la conosci:

((1 << nk) - 1) / ((1 << k) - 1)

Nel codice, la funzione Ӝassume valori n e k e calcola questa formula.

Tenere traccia dei numeri usati

Ci sono n ² numeri nella griglia finale e ogni numero può essere uno qualsiasi di n valori possibili. Al fine di tenere traccia di quali numeri sono ammessi in ciascuna cella, manteniamo un numero costituito da n ³ bit, in cui un bit è impostato per indicare che viene preso un determinato valore. Inizialmente questo numero è 0, ovviamente.

L'algoritmo inizia nell'angolo in basso a destra. Dopo aver "indovinato" il primo numero è uno 0, dobbiamo tenere traccia del fatto che lo 0 non è più consentito in nessuna cella lungo la stessa riga, colonna e diagonale:

LSB                               (example n=5)
 ↓
 10000 00000 00000 00000 10000
 00000 10000 00000 00000 10000
 00000 00000 10000 00000 10000
 00000 00000 00000 10000 10000
 10000 10000 10000 10000 10000
                             ↑
                            MSB

A tal fine, calcoliamo i seguenti quattro valori:

  • Riga corrente: abbiamo bisogno di n bit ogni n -esimo bit (uno per cella), quindi spostarlo sulla riga corrente r , ricordando che ogni riga contiene n ² bit:

    ((1 << n²) −1) / ((1 << n) −1) << n²r

  • Colonna corrente: abbiamo bisogno di n bit ogni n ²-esimo (uno per riga), quindi spostarlo sulla colonna corrente c , ricordando che ogni colonna contiene n bit:

    ((1 << n³) −1) / ((1 << n²) −1) << n²r

  • Diagonale in avanti: abbiamo bisogno di n bit ogni ... (hai prestato attenzione? Veloce, capisci!) ... n ( n +1) -th bit (ben fatto!), Ma solo se siamo effettivamente attivi la diagonale in avanti:

    ((1 << n² (n + 1)) - 1) / ((1 << n (n + 1)) - 1) se c = r

  • Diagonale all'indietro: due cose qui. Innanzitutto, come facciamo a sapere se siamo sulla diagonale all'indietro? Matematicamente, la condizione è c = ( n - 1) - r , che è la stessa di c = n + (- r - 1). Ehi, ti ricorda qualcosa? Esatto, è un complemento a due, quindi possiamo usare la negazione bit per bit (molto efficiente a Funciton) invece del decremento. In secondo luogo, la formula sopra presuppone che vogliamo impostare il bit meno significativo, ma nella diagonale all'indietro non lo facciamo, quindi dobbiamo spostarlo verso l'alto di ... lo sai? ... Esatto, n ( n - 1).

    ((1 << n² (n-1)) - 1) / ((1 << n (n-1)) - 1) << n (n-1) se c = n + ¬r

    Questo è anche l'unico in cui potenzialmente dividiamo per 0 se n = 1. Tuttavia, a Funciton non importa. 0 ÷ 0 è solo 0, non lo sai?

Nel codice, la funzione Җ(quella in basso) prende n e un indice (da cui calcola r per c per divisione e resto), calcola questi quattro valori e orli mette insieme.

L'algoritmo a forza bruta

L'algoritmo a forza bruta è implementato da Ӂ(la funzione in alto). Prende n (la dimensione della griglia), indice (dove nella griglia stiamo attualmente posizionando un numero) e preso (il numero con n ³ bit che ci dice quali numeri possiamo ancora posizionare in ogni cella).

Questa funzione restituisce una sequenza di stringhe. Ogni stringa è una soluzione completa alla griglia. È un risolutore completo; restituirebbe tutte le soluzioni se lo lasciate, ma le restituirà come una sequenza valutata in modo pigro.

  • Se l' indice ha raggiunto 0, abbiamo riempito con successo l'intera griglia, quindi restituiamo una sequenza contenente la stringa vuota (un'unica soluzione che non copre nessuna delle celle). La stringa vuota è 0e usiamo la funzione libreria per trasformarla in una sequenza a elemento singolo.

  • Il controllo descritto sotto il miglioramento delle prestazioni di seguito avviene qui.

  • Se l' indice non ha ancora raggiunto 0, lo diminuiamo di 1 per ottenere l'indice in cui ora è necessario posizionare un numero (chiamare quel ix ).

    Usiamo per generare una sequenza pigra contenente i valori da 0 a n - 1.

    Quindi usiamo ɓ(monadic bind) con un lambda che esegue le seguenti operazioni:

    • Prima guarda il bit rilevante preso per decidere se il numero è valido qui o no. Possiamo inserire un numero i se e solo se preso e (1 << ( n × ix ) << i ) non è già impostato. Se impostato, torna 0(sequenza vuota).
    • Utilizzare Җper calcolare i bit corrispondenti alla riga, colonna e diagonale corrente. Sposta da i e poi orsu preso .
    • Chiamata ricorsiva Ӂper recuperare tutte le soluzioni per le celle rimanenti, passandole la nuova presa e la ix decrementata . Ciò restituisce una sequenza di stringhe incomplete; ogni stringa ha caratteri ix (la griglia riempita fino all'indice ix ).
    • Utilizzare ɱ(mappa) per esaminare le soluzioni così trovate e utilizzare per concatenare i alla fine di ciascuna. Aggiungi una nuova riga se l' indice è un multiplo di n , altrimenti uno spazio.

Generare il risultato

Il programma principale chiama Ӂ(il bruto forcer) con n , indice = n ² (ricorda che riempiamo la griglia all'indietro) e preso = 0 (inizialmente non viene preso nulla). Se il risultato è una sequenza vuota (nessuna soluzione trovata), genera la stringa vuota. Altrimenti, genera la prima stringa nella sequenza. Si noti che ciò significa che valuterà solo il primo elemento della sequenza, motivo per cui il solutore non continua fino a quando non ha trovato tutte le soluzioni.

Miglioramento delle prestazioni

(Per coloro che hanno già letto la vecchia versione della spiegazione: il programma non genera più una sequenza di sequenze che deve essere trasformata separatamente in una stringa per l'output; genera semplicemente una sequenza di stringhe direttamente. Ho modificato la spiegazione di conseguenza Ma questo non è stato il principale miglioramento. Ecco che arriva.)

Sulla mia macchina, l'exe compilato della prima versione ha impiegato praticamente esattamente 1 ora per risolvere n = 7. Questo non era entro il limite di tempo di 10 minuti, quindi non mi sono riposato. (Beh, in realtà, il motivo per cui non mi sono riposato è perché avevo questa idea su come accelerare massicciamente.)

L'algoritmo come descritto sopra interrompe la sua ricerca e torna indietro ogni volta che incontra una cella in cui sono impostati tutti i bit nel numero preso , indicando che nulla può essere inserito in questa cella.

Tuttavia, l'algoritmo continuerà a riempire inutilmente la griglia fino alla cella in cui sono impostati tutti quei bit. Sarebbe molto più veloce se potesse fermarsi non appena una cella ancora da compilare ha già tutti i bit impostati, il che indica già che non possiamo mai risolvere il resto della griglia, indipendentemente dai numeri che abbiamo inserito esso. Ma come si fa a verificare in modo efficiente se una cella ha i suoi n bit impostati senza esaminarli tutti?

Il trucco inizia aggiungendo un singolo bit per cella al numero preso . Invece di quello che è stato mostrato sopra, ora assomiglia a questo:

LSB                               (example n=5)
 ↓
 10000 0 00000 0 00000 0 00000 0 10000 0
 00000 0 10000 0 00000 0 00000 0 10000 0
 00000 0 00000 0 10000 0 00000 0 10000 0
 00000 0 00000 0 00000 0 10000 0 10000 0
 10000 0 10000 0 10000 0 10000 0 10000 0
                                       ↑
                                      MSB

Invece di n ³, ora ci sono n ² ( n + 1) bit in questo numero. La funzione che popola l'attuale riga / colonna / diagonale è stata modificata di conseguenza (in realtà, completamente riscritta per essere onesti). Quella funzione popolerà comunque solo n bit per cella, quindi il bit extra che abbiamo appena aggiunto sarà sempre 0.

Ora, supponiamo di essere a metà del calcolo, abbiamo appena inserito una 1nella cella centrale e il numero preso assomiglia a questo:

                 current
LSB              column           (example n=5)
 ↓                 ↓
 11111 0 10010 0 01101 0 11100 0 11101 0
 00011 0 11110 0 01101 0 11101 0 11100 0
 11111 0 11110 0[11101 0]11100 0 11100 0    ← current row
 11111 0 11111 0 11111 0 11111 0 11111 0
 11111 0 11111 0 11111 0 11111 0 11111 0
                                       ↑
                                      MSB

Come puoi vedere, la cella in alto a sinistra (indice 0) e la cella in mezzo a sinistra (indice 10) sono ora impossibili. Come lo determiniamo nel modo più efficiente?

Considera un numero in cui è impostato il 0 ° bit di ogni cella, ma solo fino all'indice corrente. Tale numero è facile da calcolare usando la formula familiare:

((1 << (n + 1) i) - 1) / ((1 << (n + 1)) - 1)

Cosa otterremmo se sommassimo questi due numeri insieme?

LSB                                               LSB
 ↓                                                 ↓
 11111 0 10010 0 01101 0 11100 0 11101 0           10000 0 10000 0 10000 0 10000 0 10000 0        ╓───╖
 00011 0 11110 0 01101 0 11101 0 11100 0     ║     10000 0 10000 0 10000 0 10000 0 10000 0            ║
 11111 0 11110 0 11101 0 11100 0 11100 0  ═══╬═══  10000 0 10000 0 00000 0 00000 0 00000 0  ═════   ╓─╜
 11111 0 11111 0 11111 0 11111 0 11111 0     ║     00000 0 00000 0 00000 0 00000 0 00000 0  ═════   ╨
 11111 0 11111 0 11111 0 11111 0 11111 0           00000 0 00000 0 00000 0 00000 0 00000 0          o
                                       ↑                                                 ↑
                                      MSB                                               MSB

Il risultato è:

             OMG
              ↓
        00000[1]01010 0 11101 0 00010 0 00011 0
        10011 0 00001 0 11101 0 00011 0 00010 0
═════   00000[1]00001 0 00011 0 11100 0 11100 0
═════   11111 0 11111 0 11111 0 11111 0 11111 0
        11111 0 11111 0 11111 0 11111 0 11111 0

Come puoi vedere, l'aggiunta trabocca nel bit extra che abbiamo aggiunto, ma solo se tutti i bit per quella cella sono impostati! Pertanto, tutto ciò che resta da fare è mascherare quei bit (stessa formula di cui sopra, ma << n ) e verificare se il risultato è 0:

00000[1]01010 0 11101 0 00010 0 00011 0    ╓╖    00000 1 00000 1 00000 1 00000 1 00000 1         ╓─╖ ╓───╖
10011 0 00001 0 11101 0 00011 0 00010 0   ╓╜╙╖   00000 1 00000 1 00000 1 00000 1 00000 1        ╓╜ ╙╖    ║
00000[1]00001 0 00011 0 11100 0 11100 0   ╙╥╥╜   00000 1 00000 1 00000 0 00000 0 00000 0  ═════ ║   ║  ╓─╜
11111 0 11111 0 11111 0 11111 0 11111 0   ╓╜╙╥╜  00000 0 00000 0 00000 0 00000 0 00000 0  ═════ ╙╖ ╓╜  ╨
11111 0 11111 0 11111 0 11111 0 11111 0   ╙──╨─  00000 0 00000 0 00000 0 00000 0 00000 0         ╙─╜   o

Se non è zero, la griglia è impossibile e possiamo fermarci.

  • Schermata che mostra la soluzione e il tempo di esecuzione per n = da 4 a 7.

3
CAZZO SANTO. Amico, è impressionante.
Deusovi il

1
Secondo il commento di @ Deusovi, grazie per aver fatto così tanto sforzo in questo
hargasinski,

7

Haskell, 790 * 0,80 = 632 byte

import Data.List
import Control.Monad
import Data.Array
s r=let{h as bs=[(a,b)|a<-as,b<-bs];(&)m k=(\(Just x)->x)$lookup k m;j=Just;n=Nothing;c=[1..r];q=delete;u=h[1..r]c;o=[(s,[u |u<-[h[1..r][c]|c<-c]++[h[r]c|r<-[1..r]]++[zip[1..r][1..r],zip[1..r][r,r-1..1]],s`elem`u])|s<-u];k=foldr(>=>)j;a p d g0=k[t p d2|d2<-q d(g0!p)]g0;t p d g0|not(d`elem`(g0!p))=j g0|[]<-v=n|[d2]<-v=k[t s2 d2|s2<-[(s,delete s$nub(concat(o&s)))|s<-u]&p]g1|True=k[l[s|s<-u,not(d`elem`v)]|u<-o&p]g1 where{v=q d(g0!p);g1=g0//[(p,v)];l[]_=n;l[d3]g=a d3 d g;l _ r=j r};w g0|and[case g0!s of{[_]->True;_->False}|s<-u]=j g0|True=msum[a s' d g0>>=w|d<-g0!s']where(_,s')=minimumBy(\(a,_)(b,_)->compare a b)[(l,s)|s<-u,let v=g0!s;l=length v,l>1]}in fmap(fmap(\[x]->x))$w$array((1,1),(r,r))[((i,j),[1..r])|i<-[1..r],j<-[1..r]]

Ho notato che questo problema è molto simile al sudoku. Ricordo un vecchio risolutore di sudoku che ho scritto in Haskell basato su quest'altro in Python. Questo è il mio primo messaggio o tentativo di golf in codice.

Questo soddisfa il bonus perché ritorna Nothingper n=2,3e Just <result>per n>=4, dove si <result>trova una matrice 2D di valori integrali.

Vedi qui per l'interprete online. Tale codice è in realtà più lungo di quello nel post perché l'interprete online ha requisiti più severi su ciò che costituisce un programma completo (le regole dicono che un invio può essere una funzione). Questa presentazione accetta input come argomento di funzione.


1
Alcuni suggerimenti rapidi: a) definisci c=[1..r], in modo da poterlo usare all'interno oe w. b) minimumBy(\(a,_)(b,_)->compare a b)[...]è head$sortOn fst[...]. c) la vin v=g0!sviene utilizzato una sola volta, in modo da non definire affatto: l=length$g0!s. d) hai ancora qualche nome di parametro di due lettere. e) sostituire Truecon 1<2e Falsecon 2<1. f) and[case g0!s of{[_]->True;_->False}|s<-u]è all((==1).length.(g0!))u.
nimi,

Suggerimenti rapidi, parte II: g) (&)m k=possono essere infisso definiti: m&k=. h) not(delem (g0!p))è notElem d$g0!p. i) concat(...)è id=<<(...). j) utilizzare un operatore infix per h, ad es as%bs=.
nimi,

3
Meta suggerimenti rapidi: puoi delimitare il codice che contiene backtick correttamente usando doppi backtick ​``like`this``​!
Lynn,

4

Pyth, 41 byte

#Km.SQQI.AmqQl{d+.TK.Tm,@@Kdd@@Kt-QddQB;K
#                                      ;   # while(True)
 Km.SQQ                                    # K = random QxQ 2d list
       I                               ;   # if ...
        .A                                 # all of...
          m                          Q     # map(range(Q))...
                +                          # concat
                 .TK                       # transpose K
                    .Tm,@@Kdd@@Kt-Qdd      # diagonals of K
                      m             d      # map(range(d))
                       ,                   # 2-elem list of...
                        @@Kdd              # K[n][n]
                             @@Kt-Qd       # and K[len(K)-n-1][n]
                    .T                     # transpose
           qQl{d                           # subarrays have no dups...
                                      B;   # ... then, break
                                        K  # output final result

Forza bruta ftw!

Dal momento che questo continua a provare a mescolare casualmente fino a quando non funziona (bene, continua a provare n * [shuffle(range(n))]), ci vuole molto, molto tempo. Ecco alcuni test per darti un'idea di quanto tempo ci vuole:

llama@llama:~$ time echo 4 | pyth <(echo '#Km.SQQI.AmqQl{d+.TK.Tm,@@Kdd@@Kt-QddQB;K')               
[[2, 1, 0, 3], [0, 3, 2, 1], [3, 0, 1, 2], [1, 2, 3, 0]]
echo 4  0.00s user 0.00s system 0% cpu 0.001 total
pyth <(echo '#Km.SQQI.AmqQl{d+.TK.Tm,@@Kdd@@Kt-QddQB;K')  0.38s user 0.00s system 96% cpu 0.397 total

È solo 4x4 e funziona in poco meno di mezzo secondo. In realtà sto tradendo perché questo è il migliore di alcune prove - la maggior parte di loro prende il controllo di un secondo o due.

Devo ancora ottenere un tempismo su 5x5 (è andato a buon fine una volta, ma quello era in un REPL e non lo stavo programmando).

Si noti che la regola per il limite di tempo è stata modificata nella domanda solo dopo che questa risposta è stata pubblicata.


Suppongo che questo non possa fare 7x7 entro dieci minuti? ^^
Lynn,

@Mauris Beh, a volte può ...;) È un requisito che ho perso? Non vedo nulla che menzioni un limite di tempo nella domanda.
Maniglia della porta

Lo vedo nei commenti (non un nuovo commento, 12 ore fa)
edc65,

Mi dispiace, non ci ho pensato fino a quando qualcuno non l'ha menzionato, modificherò la sfida ora
hargasinski,

1
+1 per l'arte astratta ASCII nella tua versione commentata. :)
Ilmari Karonen il

3

SWI-Prolog, 326 * 0,80 = 260,8 byte

:-use_module(library(clpfd)).
a(N):-l(N,R),m(l(N),R),append(R,V),V ins 1..N,transpose(R,C),d(0,R,D),maplist(reverse,R,S),d(0,S,E),m(m(all_distinct),[R,C,[D,E]]),m(label,R),m(p,R).
l(L,M):-length(M,L).
d(X,[H|R],[A|Z]):-nth0(X,H,A),Y is X+1,(R=[],Z=R;d(Y,R,Z)).
p([H|T]):-write(H),T=[],nl;write(' '),p(T).
m(A,B):-maplist(A,B).

Modifica: salvato 16 byte grazie a @Mat

uso

Chiama il a(5).tuo interprete per N=5. Questo ritorna falseper N=2o N=3.

Poiché utilizza la libreria CLPFD, questa non è pura forza bruta. Questo programma può trovare una soluzione per N=20circa 15 secondi sul mio computer.

Ungolfed + spiegazioni:

Funziona sostanzialmente come un solutore di Sudoku, tranne per il fatto che i vincoli dei blocchi vengono sostituiti con i vincoli delle diagonali.

:-use_module(library(clpfd)).      % Import Constraints library

a(N):-
    l(N,R),                        % R is a list of length N
    maplist(l(N),R),               % R contains sublists, each of length N
    append(R,V),                   
    V ins 1..N,                    % Each value in the matrix is between 1 and N
    maplist(all_distinct,R),       % Values must be different on each row
    transpose(R,C),
    maplist(all_distinct,C),       % Values must be different on each column
    d(0,R,D),
    maplist(reverse,R,S),          
    d(0,S,E),
    all_distinct(D),               % Values must be different on the diagonal
    all_distinct(E),               % Values must be different on the "anti"-diagonal
    maplist(label,R),              % Affects actual values to each element
    maplist(p,R).                  % Prints each row

l(L,M):-length(M,L).               % True if L is the length of M

d(X,[H|R],[A|Z]):-nth0(X,H,A),Y is X+1,(R=[],Z=R;d(Y,R,Z)). % True if the third argument is the diagonal of the second argument

p([H|T]):-write(H),T=[],nl;write(' '),p(T).  % Prints a row separated by spaces and followed by a new line

Molto bella! È possibile salvare un byte con:maplist(maplist(all_distinct), [R,C,D,E])
mat

1
@mat Grazie per il suggerimento, salva 16 byte. Devo usare [R,C,[D,E]]però, perché Ee Dsono semplici elenchi.
Fatalizza il

Bene, molto bella soluzione!
mat

2
@Fatalize Solo per farti sapere, la tua soluzione è stata la più impressionante in quanto è l'unica che va a risolversiN=20
hargasinski,

1
@Zequ Grazie! Ma ciò è dovuto principalmente alla straordinaria libreria CLPFD di Prolog, che risolve pesantemente problemi come questi :)
Fatalizza il

2

CJam, 87 byte - 20% di bonus = 69,6 byte

qi__"@I/l
ŤˏūȻ
܀ᅀ൹৽჈͚
㑢鴑慚菥洠㬝᚜
"N/=:i0+\,m!f=`1LL](4e<=

Hardcodes le risposte. Contiene alcuni non stampabili. Funziona per N = 1attraverso N = 8.

Fondamentalmente, ogni riga di quella misteriosa stringa contiene indici nell'elenco di permutazioni di range(N), codificati come caratteri Unicode.

=:i0+\,m!f=indicizza nell'elenco delle permutazioni, aggiungendo prima uno 0 alla fine dell'elenco di indici, che rappresenta la riga inferiore 0 1 2 ... N-1. Per N < 4, l'array 2D risultante è senza senso.

`1LL]crea un elenco [N, pretty output, 1, "", ""]. Quindi, (4e<=estrae il primo elemento da questo elenco Ne recupera il min(N, 4) % 4th elemento dal resto di esso. Perché N >= 4, questo è l'output, e altrimenti sono gli output di casi speciali per i primi tre casi.

Provalo qui .


0

C ++, 672 * 0,80 645 * 0,80 = 516 byte

#include <iostream>
int **b,**x,**y,*d,*a,n;
#define R(i) for(i=0;i<n;i++){
#define E(e) e=new int[n];
int f(int c,int r) {int i=0,p=c-1;if(r>=n)return 1;if(c == n + 1)return f(1,r+1);R(i)int m=r==i,s=r+i==n-1;if(!b[r][i]&&!x[r][p]&&!(m&&d[p])&&!(s&&a[p])&&!y[i][p]){b[r][i]=c;x[r][p]=1;y[i][p]=1;if(m)d[p]=1;if(s)a[p]=1;if(f(c+1,r))return 1;b[r][i]=0;x[r][p]=0;y[i][p]=0;if(m)d[p]=0;if(s)a[p]=0;}}return 0;}
int main(){std::cin>>n;int i=0,j=0;b=new int*[n];x=new int*[n];y=new int*[n];E(d);E(a);R(i)E(b[i]);E(x[i]);E(y[i]); d[i]=0;a[i]=0;R(j)b[i][j]=0;x[i][j]=0;y[i][j]=0;}}if(f(1,0)){R(i)R(j)std::cout<<b[i][j];}std::cout<<std::endl;}}}

Provalo online qui

Dato che un paio di risposte sono già state pubblicate, ho pensato di pubblicare la versione golf del codice che ho usato per generare l'output per gli esempi. Questa è la prima volta che rispondo a un , quindi tutti i feedback sono i benvenuti. :)

Ungolfed + spiegazioni:

In sostanza, il codice sta forzando brutalmente una soluzione. Inizia nella prima riga, con 0. Inizia nel primo punto, se quel punto supera tutti i controlli, passa al numero successivo. Se riempie la riga, passa alla riga successiva. Se ha eseguito tutte le righe, significa che è stata trovata una soluzione. Se il punto non supera tutti i controlli, passa al punto successivo. Se ha terminato la riga, torna indietro, poiché un numero in una delle righe precedenti impedisce che una soluzione sia possibile.

#include <iostream>

// global variables to save bytes on passing these are function arguments
int **b, // this will store the state of the board
    **x, // if x[i][j] is true, row i of b contains the number j
    **y, // if y[i][j] is true, column i of b contains the number j
    *d,  // if d[i] the main diagonal of b contains i
    *a,  // if a[i] the antidiagonal of a contains i
    n;

// preprocessor to save bytes on repeated statements
#define R(i) for(i=0;i<n;i++){
#define E(e) e=new int[n];

// Recursively looks for a solution 
// c - the current number to insert in row r
// r - the current row to fill
int f (int c, int r) {
        int i=0,p=c-1;
        if (r >= n) return 1;             // we are done
        if (c == n + 1) return f(1,r+1);  // this row is full, move to the next row
        R(i)                              // go through the positions in this row,
                                                                            // trying to fill them with c
                int m=r==i, s=r+i==n-1;   // check if this position (r,i) is on ones
                                                                            // of the diagonals
                // if this spot isn't filled, and the row (r), column (i) and diagonals
                // (if it's on the diagonal) doesn't contain the number, fill the spot
                if (!b[r][i] && !x[r][p] && !(m&&d[p]) && !(s&&a[p]) && !y[i][p]) {
                        // fill the spot, and indicate that this row, column and diagonals 
                        // contain this number, c
                        b[r][i]=c; x[r][p]=1; y[i][p]=1;
                        if (m) d[p]=1; if (s)a[p]=1;

                        // move onto to the next number, if you find a solution, stop
                        if (f(c+1,r)) return 1;

                        // with this number in this spot, a solution is impossible, clear
                        // its, and clear the checks
                        b[r][i]=0; x[r][p]=0; y[i][p]=0;
                        if (m) d[p]=0; if (s) a[p]=0;
                }
        }

        return 0; // a solution wasn't found
}

int main() {
        std::cin >> n; // get n from STDIN

        // initialization 
        int i=0,j=0;
        b=new int*[n]; x=new int*[n]; y=new int*[n];
        E(d); E(a);
        R(i)
                E(b[i]); E(x[i]); E(y[i]); // initialization the inner arrays of b, x, y
                d[i]=0; a[i]=0;

                R(j)
                        b[i][j]=0; x[i][j]=0; y[i][j]=0; // ensure each point is initialized as 0
                }
        }

        // find a solution starting at the top-left corner and print it if it finds one
        if (f(1,0)) {
                R(i)
                        R(j)
                                std::cout<<b[i][j];
                        }
                        std::cout<<std::endl;
                }
        }
}

Dopo aver riletto il codice, mi sono reso conto che alcuni dei controlli potrebbero non essere necessari, come ad esempio il if (x[r][p]) return f(c+1,r);. Sto lavorando per accorciarlo.
Hargasinski,

0

Clojure, (215 + 258) * 0,8 = 378,4 (174 + 255) * 0,8 = 343,2

Diviso in due parti: conteggio errori Se una funzione anonima che esegue l'ottimizzazione effettiva tramite la ricerca Tabu .

Aggiornamento: più breve S(conta valori distinti all'interno dei gruppi), meno stato iniziale ottimale (nessun shuffle).

(defn S[I n](count(mapcat set(vals(apply merge-with concat(flatten(for[R[(range n)]i R j R v[[(I(+(* n i)j))]]][{[1 i]v}{[2 j]v}(if(= i j){3 v})(if(=(- n 1 i)j){4 v})])))))))
#(if-let[s({1[0]2()3()}%)]s(loop[I(vec(for[R[(range %)]i R j R]i))P #{}](let[[s I](last(sort-by first(for[R[(range(count I))]i R j R I[(assoc(assoc I i(I j))j(I i))]:when(not(P I))][(S I %)I])))](if(=(*(+(* % 2)2)%)s)(partition % I)(recur I(conj P I))))))

I benchmark single core (in millisecondi) per 4, 5, 6 e 7 vengono eseguiti 3 volte:

[[  131.855337   132.96267    138.745981]
 [ 1069.187325  1071.189488  1077.339372]
 [ 9114.736987  9206.65368   9322.656693]
 [36546.309408 36836.567267 36928.346312]]

Originale:

(defn S[I n](apply +(flatten(for[p(concat(partition n I)(for[p(apply map vector(partition n(range(count I))))](map I p))[(take-nth(inc n)I)][(rest(butlast(take-nth(dec n)I)))])](remove #{1}(vals(frequencies p)))))))
#(if-let[s({1[0]2()3()}%)]s(loop[I(vec(flatten(map shuffle(repeat %(range %)))))P #{}](let[[s I](first(sort-by first(for[R[(range(count I))]i R j R I[(assoc(assoc I i(I j))j(I i))]:when(not(P I))][(S I %)I])))](if(= s 0)(partition % I)(recur I(conj P I))))))

Vorrei che Sfosse più breve, ma poiché conta solo le occorrenze di più di una / partizione il criterio di arresto è semplice (= s 0).

Molti cicli di CPU sono sprecati per gli swap non utili, ad esempio, non migliora il punteggio se si scambia 2con 2, e non è necessario il numero di scambio tra le righe come tutti hanno valori distinti per cominciare.

Benchmark con Intel 6700K (in millisecondi):

(defn S[I n]( ... )
(def F #( ... ))

(defmacro mytime[expr]
  `(let [start# (. System (nanoTime)) ret# ~expr]
     (/ (double (- (. System (nanoTime)) start#)) 1000000.0)))

(pprint(vec(for[n[4 5 6 7]](vec(sort(repeatedly 5 #(mytime (F n)))))))

[[  43.445902   45.895107   47.277399   57.681634    62.594037]
 [ 222.964582  225.467034  240.532683  330.237721   593.686911]
 [2285.417473 2531.331068 3002.597908 6361.591714  8331.809410]
 [3569.62372  4779.062486 5725.905756 7444.941763 14120.796615]]

Multithread con pmap:

[[   8.881905  16.343714   18.87262  18.9717890   22.194430]
 [  90.963870 109.719332  163.00299  245.824443  385.365561]
 [ 355.872233 356.439256 1534.31059 2593.482767 3664.221550]
 [1307.727115 1554.00260 2068.35932 3626.878526 4029.543011]]
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.