Riesci a battere l'Intelligence britannica? (Risolutore Nonogram)


20

È tempo di intraprendere una pericolosa ricerca per sconfiggere l'Intelligence britannica. Lo scopo di questa sfida è quello di scrivere il codice più breve che risolverà un Nonogram.

Che cos'è un nonogramma?

Nonogram Puzzle

Le regole sono semplici Hai una griglia di quadrati, che deve essere riempita in nero o lasciata vuota. Accanto a ciascuna riga della griglia sono elencate le lunghezze delle serie di quadrati neri su quella riga. Sopra ogni colonna sono elencate le lunghezze delle serie di quadrati neri in quella colonna. Il tuo obiettivo è trovare tutti i quadrati neri. In questo tipo di puzzle, i numeri sono una forma di tomografia discreta che misura quante linee ininterrotte di quadrati riempiti ci sono in una data riga o colonna. Ad esempio, un indizio di "4 8 3" significherebbe che ci sono gruppi di quattro, otto e tre quadrati pieni, in quell'ordine, con almeno un quadrato vuoto tra gruppi successivi. [ 1 ] [ 2 ]

Quindi la soluzione al nonogramma di cui sopra sarebbe:

Nonogramma risolto

Dettagli di implementazione

Puoi scegliere di rappresentare il Nonogram come preferisci e prenderlo come input nel modo che ritieni adatto alla tua lingua. Lo stesso vale per l'output. Lo scopo di questa sfida è letteralmente portare a termine il lavoro; se riesci a risolvere il nonogramma con qualunque output del tuo programma, questo è valido. Un avvertimento è che non puoi usare un risolutore online :)

Questo problema è molto impegnativo dal punto di vista algoritmico (np-complete) in quanto non esiste una soluzione completamente efficiente e come tale, non sarai penalizzato per non essere in grado di risolverne di più grandi, anche se la tua risposta sarà fortemente ricompensata se lo è in grado di gestire casi importanti (vedi bonus). Come punto di riferimento, la mia soluzione funziona fino a circa 25x25 entro 5-10 secondi. Per consentire flessibilità tra le diverse lingue, le soluzioni che richiedono meno di 5 minuti per un nonogramma 25x25 sono abbastanza buone.

Puoi assumere un puzzle sempre in un nonogramma NxN quadrato.

Puoi utilizzare questo creatore di puzzle nonogramma online per testare le tue soluzioni.

punteggio

Naturalmente, sei libero di usare qualsiasi lingua tu voglia e dato che questo è il golf del codice, le voci saranno ordinate nell'ordine: accuracy -> length of code -> speed.Tuttavia, non scoraggiarti dal linguaggio del golf, risposte in tutte le lingue che mostrano tentativi di golf in modo interessante sarà votato!

indennità

In realtà ho imparato a conoscere Nonograms da una cartolina di Natale crittografica pubblicata dall'Intelligence britannica qui . La prima parte era sostanzialmente un Nonogram 25x25 massiccio. Se la tua soluzione è in grado di risolverlo, otterrai dei complimenti :)

Per semplificarti la vita in termini di immissione dei dati, ho fornito il modo in cui ho rappresentato i dati di questo specifico puzzle per il tuo uso gratuito. Le prime 25 linee sono gli indizi delle righe, seguite da una linea di separazione "-", seguite da 25 linee degli indizi col, seguite da una linea di separazione "#", quindi da una rappresentazione della griglia con gli indizi quadrati compilati.

7 3 1 1 7
1 1 2 2 1 1
1 3 1 3 1 1 3 1
1 3 1 1 6 1 3 1
1 3 1 5 2 1 3 1
1 1 2 1 1
7 1 1 1 1 1 7
3 3
1 2 3 1 1 3 1 1 2
1 1 3 2 1 1
4 1 4 2 1 2
1 1 1 1 1 4 1 3
2 1 1 1 2 5
3 2 2 6 3 1
1 9 1 1 2 1
2 1 2 2 3 1
3 1 1 1 1 5 1
1 2 2 5
7 1 2 1 1 1 3
1 1 2 1 2 2 1
1 3 1 4 5 1
1 3 1 3 10 2
1 3 1 1 6 6
1 1 2 1 1 2
7 2 1 2 5
-
7 2 1 1 7
1 1 2 2 1 1
1 3 1 3 1 3 1 3 1
1 3 1 1 5 1 3 1
1 3 1 1 4 1 3 1
1 1 1 2 1 1
7 1 1 1 1 1 7
1 1 3
2 1 2 1 8 2 1
2 2 1 2 1 1 1 2
1 7 3 2 1
1 2 3 1 1 1 1 1
4 1 1 2 6
3 3 1 1 1 3 1
1 2 5 2 2
2 2 1 1 1 1 1 2 1
1 3 3 2 1 8 1
6 2 1
7 1 4 1 1 3
1 1 1 1 4
1 3 1 3 7 1
1 3 1 1 1 2 1 1 4
1 3 1 4 3 3
1 1 2 2 2 6 1
7 1 3 2 1 1
#
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
0 0 0 1 1 0 0 0 0 0 0 0 1 1 0 0 0 0 0 0 0 1 0 0 0
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 1 1 0 0 1 0 0 0 1 1 0 0 1 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 1 0 0 0 0 1 0 0 0 0 1 0 0 0 1 0 0 0 0
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
0 0 0 1 1 0 0 0 0 1 1 0 0 0 0 1 0 0 0 0 1 1 0 0 0
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0

Ed ecco una versione leggermente diversa per tua comodità; una tupla separata da virgola (riga, col) in cui ogni elemento è un elenco di elenchi.

([[7, 3, 1, 1, 7],
  [1, 1, 2, 2, 1, 1],
  [1, 3, 1, 3, 1, 1, 3, 1],
  [1, 3, 1, 1, 6, 1, 3, 1],
  [1, 3, 1, 5, 2, 1, 3, 1],
  [1, 1, 2, 1, 1],
  [7, 1, 1, 1, 1, 1, 7],
  [3, 3],
  [1, 2, 3, 1, 1, 3, 1, 1, 2],
  [1, 1, 3, 2, 1, 1],
  [4, 1, 4, 2, 1, 2],
  [1, 1, 1, 1, 1, 4, 1, 3],
  [2, 1, 1, 1, 2, 5],
  [3, 2, 2, 6, 3, 1],
  [1, 9, 1, 1, 2, 1],
  [2, 1, 2, 2, 3, 1],
  [3, 1, 1, 1, 1, 5, 1],
  [1, 2, 2, 5],
  [7, 1, 2, 1, 1, 1, 3],
  [1, 1, 2, 1, 2, 2, 1],
  [1, 3, 1, 4, 5, 1],
  [1, 3, 1, 3, 10, 2],
  [1, 3, 1, 1, 6, 6],
  [1, 1, 2, 1, 1, 2],
  [7, 2, 1, 2, 5]],
 [[7, 2, 1, 1, 7],
  [1, 1, 2, 2, 1, 1],
  [1, 3, 1, 3, 1, 3, 1, 3, 1],
  [1, 3, 1, 1, 5, 1, 3, 1],
  [1, 3, 1, 1, 4, 1, 3, 1],
  [1, 1, 1, 2, 1, 1],
  [7, 1, 1, 1, 1, 1, 7],
  [1, 1, 3],
  [2, 1, 2, 1, 8, 2, 1],
  [2, 2, 1, 2, 1, 1, 1, 2],
  [1, 7, 3, 2, 1],
  [1, 2, 3, 1, 1, 1, 1, 1],
  [4, 1, 1, 2, 6],
  [3, 3, 1, 1, 1, 3, 1],
  [1, 2, 5, 2, 2],
  [2, 2, 1, 1, 1, 1, 1, 2, 1],
  [1, 3, 3, 2, 1, 8, 1],
  [6, 2, 1],
  [7, 1, 4, 1, 1, 3],
  [1, 1, 1, 1, 4],
  [1, 3, 1, 3, 7, 1],
  [1, 3, 1, 1, 1, 2, 1, 1, 4],
  [1, 3, 1, 4, 3, 3],
  [1, 1, 2, 2, 2, 6, 1],
  [7, 1, 3, 2, 1, 1]])

Purtroppo il mio sito web è inattivo ma aveva un solutore Nonogram abbastanza veloce; 5-10 minuti sembra eccessivo.
Neil,


1
@dwana Non devi preoccuparti di casi irrisolvibili. Per quanto riguarda la risposta casuale, su un nonogramma 25x25, hai 2 ^ 625 possibili configurazioni. Nel contesto, questo è più del doppio del numero di atomi nell'universo noto (cioè se avessi usato un po 'ogni atomo nell'universo, non avresti ancora abbastanza spazio per memorizzare le possibilità). In termini di tempo, se ti ci volesse un secondo nano (generoso) per verificare la validità di ciascuna configurazione, occorrerebbero 7 vite nell'universo affinché il codice finisca l'esecuzione :)
gowrath,

1
Ty per chiarire casi irrisolvibili. (+ Ho un PC magico che convalida una risposta in ~ 2.1546362E-186 secondi)
dwana

1
Il tuo CSV non ha suggerimenti quadrati. Ecco alcuni JS per generarli:s=[].fill([].fill(0,0,25),0,25);s[3][3]=s[3][4]=s3[3][12]=s3[3][13]=s3[3][21]=s[8][6]=s[8][7]=s[8][10]=s[8][14]=s[8][15]=s[8][18]=s[16][6]=s[16][11]=s[16][16]=s[16][20]=s[21][3]=s[21][4]=s[21][9]=s[21][10]=s[21][15]=s[21][20]=s[21][21]=1;
Titus,

Risposte:


5

Brachylog , 70 69 byte

[R:C]hlL~l:L:1f=.:3aR,.z:3aC,
tL,?he##ElL,E:2a
.<2,_1<
@b:4f:la
e.h1,

Questo prende un elenco di due elenchi (prima gli indicatori di riga, poi quelli di colonna). Ogni indicatore è esso stesso un elenco (per le seduzioni come [3,1]su una riga).

Questa versione richiede circa 3 minuti per risolvere l'esempio 5 per 5 della sfida.

Modo molto più efficiente, 91 byte

[R:C]hlL~l:L:1f:Cz:3az:Rz:3a=.:4aR,.z:4aC,
tL,?he##ElL,E:2a
.<2,_1<
:+a#=,?h
@b:5f:la
e.h1,

Provalo online!

Questa non è una forza bruta completa: l'unica differenza è che questa impone vincoli ai valori delle celle in modo tale che il numero di 1 in ogni riga e colonna corrisponda ai numeri indicati come indicatori nell'input. L'unica parte della forza bruta è quindi quella di trovare una griglia con quei vincoli per i quali i "blocchi" di 1 corrispondono a ciò che viene dato come indicazione.

Questo richiede circa 0,05 secondi sull'esempio 5 per 5 della sfida. Questo è ancora troppo lento per il caso bonus, poiché non ho idea di come esprimere i blocchi di quelli separati da uno o più zeri in termini di vincoli.

Spiegazione

Spiegherò di seguito la versione da 93 byte. L'unica differenza tra i due è la chiamata al predicato 3 che non esiste nella versione a 70 byte e la numerazione dei predicati (poiché ce n'è uno in meno).

  • Predicato principale:

    [R:C]     Input = [R, C]
    hlL       The length of R is L
    ~l        Create a list of length L
    :L:1f     Each element of that list is a sublist of length L with cells 0 or 1 (Pred 1)
              %%% Part unique to the 93 bytes version
    :Cz       Zip the rows of the list of lists with C
    :3a       The sum of 1s in each row is equal to the sum of the indicators (Pred 3)
    z         Transpose
    :Rz       Zip the columns of the list of lists with R
    :3a       The sum of 1s in each column is equal to the sum of the indicators (Pred 3)
              %%%
    =.        Assign values to the cells of the list of lists which satisfy the constraints
    :4aR,     The blocks of 1s must match the indicators on rows
    .z        Transpose
    :4aC,     The blocks of 1s must match the indicators on columns
    
  • Predicato 1: impone che le righe abbiano una lunghezza specifica e che ogni cella sia 0 o 1.

    tL,       L is the length given as second element of the input
    ?he       Take an element from the list
    ##ElL,    That element E is itself a list of length L
    E:2a      The elements of E are 0s and 1s (Pred 2)
    
  • Predicato 2: vincola una variabile a 0 o 1

    .<2,      Input = Output < 2
    _1<       Output > -1
    
  • Predicato 3: la somma di 1 in un elenco deve essere uguale alla somma degli indicatori (ad es. Se l'indicatore è [3: 1], l'elenco deve avere la somma 4)

    :+a       Sum the elements of the list and sum the indicator
    #=,       Both sums must be equal
    ?h        Output is the list
    
  • Predicato 4: verificare che i blocchi di 1s corrispondano all'indicatore

    @b        Split the list in blocks of the same value
    :5f       Find all blocks of 1s (Pred 5)
    :la       The list of lengths of the blocks results in the indicator (given as output)
    
  • Predicato 5: vero per blocchi di 1s, falso altrimenti

    e.        Output is an element of the input
      h1,     Its first value is 1
    

Sembra lo strumento perfetto per il lavoro. In attesa di una spiegazione.
Emigna,

@Fatalize Questo è fantastico, stavo aspettando che qualcuno usasse un linguaggio da prologo per farlo. Hai provato con il caso 25x25? Ho inserito i dati per voi già
gowrath

@gowrath Questo pomeriggio lo eseguirò sul mio computer, vedremo cosa succede.
Fatalizza il

@Fatalize Sembra scadere ma potrei sbagliarmi.
Nemmeno io mi affiderei

@gowrath Timeout su TIO, ma lo eseguirò sull'interprete offline direttamente sul mio computer.
Fatalizza il

9

Haskell, 242 230 201 199 177 163 160 149 131 byte

import Data.Lists
m=map
a#b=[x|x<-m(chunk$length b).mapM id$[0,1]<$(a>>b),g x==a,g(transpose x)==b]
g=m$list[0]id.m sum.wordsBy(<1)

Finalmente sotto i 200 byte, credito a @Bergi. Enorme grazie a @nimi per aver contribuito a dimezzare le dimensioni.

Wow. Quasi a metà grandezza ora, in parte a causa mia, ma principalmente a causa di @nimi.

La funzione magica è (#). Trova tutte le soluzioni di un dato nonogramma.

Questo è in grado di risolvere tutti i casi, ma può essere super lento, poiché la sua complessità è circa O(2^(len a * len b)). Un rapido benchmark ha rivelato 86 GB allocati per un nonogramma 5x5.

Curiosità: funziona con tutti i nonogrammi, non solo quelli quadrati.


Come funziona:

  • a#b: Dati elenchi di elenchi di numeri interi che rappresentano il numero di quadrati, generano tutte le griglie ( map(chunk$length b).mapM id$a>>b>>[[0,1]]) e filtrano i risultati per mantenere solo quelli validi.
  • g: Dato un potenziale nonogramma, somma le corse di 1 in orizzontale.

Intendi O (2 ^ (len a * len b)), non O ((len a * len b) ^ 2).
Anders Kaseorg,

@AndersKaseorg Right. Mantieni il milione che ho sottinteso per errore lì. : D
ThreeFx,

1
Altri pochi byte: m(chunk$l b)ereplicate(l$a>>b)
Bergi il

@ThreeFx 86GB: O ... Btw potresti spiegare brevemente come compilare questo? Ho appena iniziato a studiare haskell e questo sta dando errori con ghc. Voglio provarlo :)
gowrath,

1
import Data.Listsè sufficiente, perché riesporta sia Data.Liste Data.List.Split.
nimi,

4

Pyth, 91 72 71 byte

D:GHdRq@@QdG.nCf.)TrH8V^,01^hQ2=TcNhQ=Y1VhQ=*Y*:H@TH1:H@CTH2)IYjbmjkdTb

Un programma che accetta l'input di un elenco del modulo in [size, [horizontal clues], [vertical clues]]cui ogni indizio è un elenco di numeri interi (gli indizi vuoti sono l'elenco vuoto []) e stampa ogni soluzione, separata da nuova riga, sotto forma di una griglia binaria dove 1è ombreggiata e non 0ombreggiata .

Questa è una forza bruta, quindi è grosso modo O(2^n^2). Inizia a impiegare molto tempo per i puzzle più grandi, ma risolverà qualsiasi dimensione arbritrale con un tempo sufficiente.

Provalo online

Come funziona

Il programma genera ogni possibile layout prendendo il prodotto cartesiano ripetuto [0, 1]con una lunghezza uguale a size^2. Questo viene quindi suddiviso in blocchi, fornendo un elenco per ogni linea orizzontale. Ogni riga è codificata per la lunghezza, filtrata dalla presenza 1e appiattita, lasciando la chiave per quella riga. Questo viene quindi verificato rispetto all'input. Il processo sopra descritto viene ripetuto per la trasposizione dei blocchi, controllando le linee verticali. In caso di hit, ogni blocco viene concatenato e i blocchi concatenati vengono uniti su nuove righe e stampati implicitamente, con una nuova riga finale.

D:GHdRq@@QdG.nCf.)TrH8V^,01^hQ2=TcNhQ=Y1VhQ=*Y*:H@TH1:H@CTH2)IYjbmjkdTb  Program. Input: Q
                            hQ                                           Q[0], size
                           ^  2                                          Square
                        ,01                                              [0, 1]
                       ^                                                 Cartesian product
                      V                                     )            For N in the Cartesian product:
                                 cNhQ                                    Split N into Q[0] chunks
                               =T                                        Assign that to T
                                     =Y1                                 Y=1
                                        VhQ                              For H in range [0, Q[0]-1]:
D:GHd                                                                     def :(G, H, d)
                   rH8                                                     Run-length-encode(H)
               f.)T                                                        Filter by presence of 1 in character part
            .nC                                                            Transpose and flatten, giving the clue
       @@QdG                                                               Q[d][G], the relevant input clue
     Rq                                                                    Return clue==input clue
                                               :H@TH1                     :(H, T, 1)
                                                     :H@CTH2              :(H, transpose(T), 2)
                                           =*Y*                           Y=Y*product of above two
                                                             IY           If Y:
                                                                 mjkdT     Conacatenate each element of T
                                                               jb          Join on newlines
                                                                      b    Add a newline and implicitly print

Grazie a @ Pietu1998 per alcuni suggerimenti


Questo potrebbe essere il programma Pyth più lungo che abbia mai visto
Business Cat,

=ZhZè uguale a =hZed FNè uguale a V.
PurkkaKoodari,

@TheBikingViking Cosa intendi esattamente con un tempo sufficiente? Sono abbastanza sicuro che questo non risolverà un 25x25 ormai se lo avessi iniziato dalla concezione dell'universo.
Gowrath,

1
@gowrath ne sono anche abbastanza sicuro! Sono nuovo di Pyth, e dopo tanto tempo non mi è nemmeno
venuto in mente di

2

Javascript (ES6), 401 386 333 byte

Questo è un primo tentativo. Non è molto efficiente, ma ero curioso di testare una soluzione usando espressioni regolari sulla rappresentazione binaria di righe e colonne.

Ad esempio, tradurrà l'indizio [3,1]nella seguente espressione regolare:

/^0*1{3}0+1{1}0*$/

In questo momento, questa versione non sta prendendo in considerazione indizi quadrati. Probabilmente lo aggiungerò più tardi.

Codice

(c,r)=>{W=c.length;w=[];S=0;M=(n,p)=>eval(`/^0*${p.map(v=>`1{${v}}`).join`0+`}0*$/`).exec(n);R=(y,i=0)=>S||(w[y]=r[y][i],y+1<W?R(y+1):c.every((c,y)=>(n=0,w.map((_,x)=>n+=w[W-1-x][y]),M(n,c)))&&(S=w.join`
`),r[y][i+1]&&R(y,i+1));r=r.map(r=>[...Array(1<<W)].map((_,n)=>((1<<30)|n).toString(2).slice(-W)).filter(n=>M(n,r)));return R(0)}

Produzione

La soluzione viene visualizzata in formato binario. Ad esempio:

00110
01110
11100
11101
00001

Test

Questo è un semplice test sulla griglia di esempio.

let f =
(c,r)=>{W=c.length;w=[];S=0;M=(n,p)=>eval(`/^0*${p.map(v=>`1{${v}}`).join`0+`}0*$/`).exec(n);R=(y,i=0)=>S||(w[y]=r[y][i],y+1<W?R(y+1):c.every((c,y)=>(n=0,w.map((_,x)=>n+=w[W-1-x][y]),M(n,c)))&&(S=w.join`
`),r[y][i+1]&&R(y,i+1));r=r.map(r=>[...Array(1<<W)].map((_,n)=>((1<<30)|n).toString(2).slice(-W)).filter(n=>M(n,r)));return R(0)}

console.log(f(
  [[2],[3],[4],[2],[2]],
  [[2],[3],[3],[3,1],[1]]
));


bella idea. uccide i miei browser sul puzzle di Natale, però.
Tito,

2

Haskell, 109 byte

Disclaimer: questo deriva dalla risposta di @ ThreeFx . L'ho aiutato a cercare la sua risposta, ma sembra che abbia perso interesse a includere i miei ultimi miglioramenti sostanziali, quindi li inserisco come una nuova risposta.

import Data.List
n=mapM id
a#b=[x|x<-n$(n$" #"<$a)<$b,g x==a,g(transpose x)==b]
g=map$max[0].map length.words

Esempio di utilizzo: [[2],[3],[3],[3,1],[1]] # [[2],[3],[4],[2],[2]]-> [[" ## "," ### ","### ","### #"," #"]].

Forza bruta. Prova tutte le combinazioni di e #, dividi i blocchi int di #, conta le lunghezze e confronta con l'input.


1

PHP, 751 833 (720) 753 724 726 710 691 680 682 byte

Ero ansioso di costruire un incremento di sequenza specializzato e provare ancora una volta il mio generatore cartesiano;
ma lasciò cadere il cartesiano in favore del backtracking per risolvere più velocemente il grande puzzle.

$p=[];foreach($r as$y=>$h){for($d=[2-($n=count($h)+1)+$u=-array_sum($h)+$w=count($r)]+array_fill($i=0,$n,1),$d[$n-1]=0;$i<1;$d[0]+=$u-array_sum($d)){$o=$x=0;foreach($d as$i=>$v)for($x+=$v,$k=$h[$i];$k--;)$o+=1<<$x++;if(($s[$y]|$o)==$o){$p[$y][]=$o;$q[$y]++;}for($i=0;$i<$n-1&$d[$i]==($i?1:0);$i++);if(++$i<$n)for($d[$i]++;$i--;)$d[$i]=1;}}
function s($i,$m){global$c,$w,$p;for(;!$k&&$i[$m]--;$k=$k&$m<$w-1?s($i,$m+1):$k){for($k=1,$x=$w;$k&&$x--;){$h=$c[$x];for($v=$n=$z=$y=0;$k&&$y<=$m;$y++)$n=$n*($f=($p[$y][$i[$y]]>>$x&1)==$v)+$k=$f?:($v=!$v)||$n==$h[$z++];if($k&$v)$k=$n<=$h[$z];}}return$k?is_array($k)?$k:$i:0;}
foreach(s($q,0)as$y=>$o)echo strrev(sprintf("\n%0{$w}b",$p[$y][$o]));
  • si aspetta suggerimenti negli array $rper suggerimenti di riga, suggerimenti $cdi colonna e $ssuggerimenti quadrati.
  • genera invalid argument supplied for foreachse non trova soluzione.
  • per ottenere il conteggio di byte corretto, utilizzare un fisico \ne rimuovere le altre due interruzioni di riga.

descrizione

1) dai suggerimenti di riga
generano possibili righe che soddisfano i suggerimenti quadrati
e ricordano i loro conteggi per ciascun indice di riga.

2) tornare indietro sulle combinazioni di righe:
se la combinazione soddisfa i suggerimenti della colonna, cercare più a fondo o restituire una combinazione riuscita,
altrimenti provare la possibilità successiva per questa riga

3) soluzione di stampa


L'ultimo golf ha avuto un forte impatto sulle prestazioni;
ma ho rimosso le assegnazioni di profilazione per i benchmark finali.

Sostituire $n=$n*($f=($p[$y][$i[$y]]>>$x&1)==$v)+$k=$f?:($v=!$v)||$n==$h[$z++];
con if(($p[$y][$i[$y]]>>$x&1)-$v){$k=($v=!$v)||$n==$h[$z++];$n=1;}else$n++;
per annullare l'ultimo passaggio del golf.

esempi

Per il piccolo esempio (da 17 a 21 circa 12 8 7 6,7 5,3 ms), utilizzare

$r=[[2],[3],[3],[3,1],[1]];$c=[[2],[3],[4],[2],[2]];$s=[0,0,0,0,0];

per il puzzle di Natale:

  • ucciso il mio piccolo server di casa con la vecchia soluzione
  • ha ucciso il browser con output di test
  • ora risolto in 50 37,8 45,5 circa 36 secondi

inserire i dati dalla domanda in un file christmas.nonograme utilizzare questo codice per importare:

$t=r;foreach(file('christmas.nonogram')as$h)if('-'==$h=trim($h))$t=c;elseif('#'==$h){$t=s;$f=count($h).b;}else
{$v=explode(' ',$h);if(s==$t)for($h=$v,$v=0,$b=1;count($h);$b*=2)$v+=$b*array_shift($h);${$t}[]=$v;}

abbattersi

$p=[];  // must init $p to array or `$p[$y][]=$o;` will fail
foreach($r as$y=>$h)
{
    // walk $d through all combinations of $n=`hint count+1` numbers that sum up to $u=`width-hint sum`
    // (possible `0` hints for $h) - first and last number can be 0, all others are >0
    for(
        $d=[2-
            ($n=count($h)+1)+               // count(0 hint)=count(1 hint)+1
            $u=-array_sum($h)+$w=count($r)  // sum(0 hint) = width-sum(1 hint)
        ]                           // index 0 to max value $u-$n+2
        +array_fill($i=0,$n,1)      // other indexes to 1
        ,$d[$n-1]=0;                // last index to 0
                                    // --> first combination (little endian)
        $i<1;   // $i:0 before loop; -1 after increment; >=$n after the last combination
        $d[0]+=$u-array_sum($d) // (see below)
    )
    {
        // A: create row (binary value) from 1-hints $h and 0-hints $d
        $o=$x=0;
        foreach($d as$i=>$v)
            for($x+=$v,$k=$h[$i];$k--;)
                $o+=1<<$x++;
        // B: if $o satisfies the square hints
        if(($s[$y]|$o)==$o)
        {
            $p[$y][]=$o;    // add to possible combinations
            $q[$y]++;       // increase possibility counter
        }
        // C: increase $d
            // find lowest index with a value>min
                // this loop doesn´t need to go to the last index:
                // if all previous values are min, there is nothing left to increase
        for($i=0;$i<$n-1&$d[$i]==($i?1:0);$i++);
        if(++$i<$n)             // index one up; increase $d if possible
            for($d[$i]++        // increase this value
            ;$i--;)$d[$i]=1;    // reset everything below to 1
            // adjust $d[0] to have the correct sum (loop post condition)
    }
}

// search solution: with backtracking on the row combinations ...
function s($i,$m)
{
    global $c,$w,$p;
    for(;
        !$k // solution not yet found
        &&$i[$m]    // if $i[$m]==0, the previous iteration was the last one on this row: no solution
            --;     // decrease possibility index for row $m
        $k=$k&$m<$w-1? s($i,$m+1) : $k      // if ok, seek deeper while last row not reached ($m<$w-1)
    )
    {
        // test if the field so far satisfies the column hints: loop $x through columns
        for($k=1,$x=$w;$k&&$x--;)   // ok while $k is true
        {
            $h=$c[$x];
            // test column hints on the current combination: loop $y through rows up to $m
            for($v=$n=$z=   // $v=temporary value, $n=temporary hint, $z=hint index
                $y=0;$k&&$y<=$m;$y++)
                // if value has not changed, increase $n. if not, reset $n to 1
                // (or 0 for $k=false; in that case $n is irrelevant)
                $n=$n*  
                    // $f=false (int 0) when value has changed, true (1) if not
                    ($f=($p[$y][$i[$y]]>>$x&1)==$v)
                    +$k=$f?:    // ok if value has NOT changed, else
                        ($v=!$v)        // invert value. ok if value was 0
                        || $n==$h[$z    // value was 1: ok if temp hint equals current sub-hint
                        ++]             // next sub-hint
                ;
            // if there is a possibly incomplete hint ($v==1)
            // the incomplete hint ($n) must be <= the next sub-hint ($c[x][$z])
            // if $n was <$h[$z] in the last row, the previous column hints would not have matched
            if($k&$v)$k=$n<=$h[$z];
        }
        // ok: seek deeper (loop post condition)
        // not ok: try next possibility (loop pre condition)
    }
    return$k?is_array($k)?$k:$i:0;  // return solution if solved, 0 if not
}

// print solution
foreach(s($q,0)as$y=>$o)echo strrev(sprintf("\n%0{$w}b",$p[$y][$o]));

1
Il grande esempio uccide il mio piccolo server di casa (500 - Errore interno del server). Le combinazioni sono pronte dopo 15 secondi, ma il prodotto cartesiano ha 1.823 E + 61 membri. (La 7a e 22a fila hanno solo una soluzione tra l'altro.) L'algoritmo deve essere migliorato.
Tito

Penso che questo potrebbe essere accelerato se si usasse il backtracking ricorsivo. Tuttavia, ottimo lavoro!
Gowrath,

@gowrath: il backtracking dà un po 'e salva anche i byte ... numeri interi con aritmetica di bit danno circa il 50% di velocità ma aumentano le dimensioni (devo ancora sapere quanto costa esattamente) ... Ci sono ancora.
Tito

@gowrath: ho inseguito il mio bug; era nell'incremento (dove altro?): $ddeve essere nell'ordine corretto perforeach
Tito
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.