¿Se puede vencer a la inteligencia británica? (Solucionador de nonogramas)


20

Es hora de embarcarse en una peligrosa búsqueda para derrotar a la inteligencia británica. El objetivo de este desafío es escribir el código más corto que resolverá un Nonograma.

¿Qué es un nonograma?

Nonogram Puzzle

Las reglas son simples. Tiene una cuadrícula de cuadrados, que debe rellenarse en negro o dejarse en blanco. Al lado de cada fila de la cuadrícula se enumeran las longitudes de las corridas de cuadrados negros en esa fila. Arriba de cada columna se enumeran las longitudes de las corridas de cuadrados negros en esa columna. Tu objetivo es encontrar todos los cuadrados negros. En este tipo de rompecabezas, los números son una forma de tomografía discreta que mide cuántas líneas continuas de cuadrados rellenos hay en una fila o columna dada. Por ejemplo, una pista de "4 8 3" significaría que hay conjuntos de cuatro, ocho y tres cuadrados rellenos, en ese orden, con al menos un cuadrado en blanco entre grupos sucesivos. [ 1 ] [ 2 ]

Entonces, la solución al Nonograma anterior sería:

Nonograma resuelto

Detalles de implementacion

Puedes elegir representar el Nonograma como quieras y tomarlo como entrada de la forma que consideres adecuada para tu idioma. Lo mismo vale para la salida. El objetivo de este desafío es, literalmente, hacer el trabajo; si puede resolver el nonograma con cualquier salida que le dé su programa, eso es válido. Una advertencia es que no puedes usar un solucionador en línea :)

Este problema es muy desafiante desde el punto de vista algorítmico (np-complete), ya que no existe una solución completamente eficiente y, como tal, no se lo penalizará por no poder resolver los más grandes, aunque su respuesta será muy recompensada si es capaz de manejar grandes casos (ver bonificación). Como punto de referencia, mi solución funciona hasta aproximadamente 25x25 en 5-10 segundos. Para permitir flexibilidad entre diferentes idiomas, las soluciones que toman menos de 5 minutos para un nonograma de 25x25 son lo suficientemente buenas.

Puede suponer un rompecabezas en un nonograma NxN cuadrado.

Puede usar este fabricante de rompecabezas de nonogramas en línea para probar sus soluciones.

Puntuación

Por supuesto, puede usar el idioma que desee y, dado que se trata de código de golf, las entradas se ordenarán en el orden: sin accuracy -> length of code -> speed.embargo, no se desanime por los idiomas de código de golf, las respuestas en todos los idiomas que muestran intentos de golf de una manera interesante será votado!

Prima

De hecho, aprendí sobre Nonogramas de una tarjeta de Navidad criptográfica lanzada por la inteligencia británica aquí . La primera parte fue básicamente un enorme Nonograma de 25x25. Si su solución puede resolver esto, recibirá felicitaciones :)

Para facilitarle la vida en términos de entrada de datos, he proporcionado cómo representé los datos para este rompecabezas específico para su uso gratuito. Las primeras 25 líneas son las pistas de fila, seguidas de una línea de separación '-', seguida de 25 líneas de las pistas de columnas, seguidas de una línea de separación '#', y luego una representación de la cuadrícula con las pistas cuadradas rellenas.

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

Y aquí hay una versión ligeramente diferente para su conveniencia; una tupla separada por comas (fila, col) donde cada elemento es una lista de listas.

([[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]])

Lamentablemente, mi sitio web no funciona, pero solía tener un solucionador de Nonogramas razonablemente rápido; 5-10 minutos suena excesivo.
Neil


1
@dwana No necesita preocuparse por casos irresolubles. En cuanto a la respuesta aleatoria, en un nonograma de 25x25, tiene 2 ^ 625 configuraciones posibles. En contexto, es más del doble del número de átomos en el universo conocido (es decir, si usara cada átomo en el universo como un bit, aún no tendría suficiente espacio para almacenar las posibilidades). En términos de tiempo, si es que se ha tomado un segundo nano (generoso) para comprobar la validez de cada configuración, se necesitarían 7 vidas del universo para el código para finalizar la ejecución :)
gowrath

1
Ty para aclarar casos insolubles. (+ tengo una PC mágica que valida una respuesta en ~ 2.1546362E-186 segundos)
dwana

1
Su CSV no tiene pistas cuadradas. Aquí hay algunos JS para generarlos: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

Respuestas:


5

Brachylog , 70 69 bytes

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

Esto toma una lista de dos listas (primero los indicadores de filas, luego los de columna). Cada indicador es en sí mismo una lista (para situaciones como [3,1]en una fila).

Esta versión tarda unos 3 minutos en resolver el ejemplo 5 por 5 del desafío.

Versión mucho más eficiente, 91 bytes

[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,

Pruébalo en línea!

Esta no es la fuerza bruta completa: la única diferencia es que esta impone restricciones en los valores de las celdas de modo que el número de 1s en cada fila y columna coincida con los números dados como indicadores en la entrada. La única parte de la fuerza bruta es encontrar una cuadrícula con esas restricciones para las cuales los "bloques" de 1 coinciden con lo que se da como indicación.

Este toma alrededor de 0.05 segundos en el ejemplo 5 por 5 del desafío. Esto todavía es demasiado lento para el caso de bonificación, ya que no tengo idea de cómo expresar los bloques de unos separados por uno o más ceros en términos de restricciones.

Explicación

Explicaré a continuación la versión de 93 bytes. La única diferencia entre los dos es la llamada al predicado 3, que no existe en la versión de 70 bytes, y la numeración de los predicados (ya que hay uno menos).

  • Predicado principal:

    [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
    
  • Predicado 1: obliga a las filas a tener una longitud específica y que cada celda sea 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)
    
  • Predicado 2: restringir una variable para que sea 0 o 1

    .<2,      Input = Output < 2
    _1<       Output > -1
    
  • Predicado 3: la suma de 1s en una lista debe ser igual a la suma de los indicadores (por ejemplo, si el indicador es [3: 1], entonces la lista debe tener la suma 4)

    :+a       Sum the elements of the list and sum the indicator
    #=,       Both sums must be equal
    ?h        Output is the list
    
  • Predicado 4: Verifique que los bloques de 1s coincidan con el indicador

    @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)
    
  • Predicado 5: verdadero para bloques de 1s, falso de lo contrario

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

Se siente como la herramienta perfecta para el trabajo. Esperando la explicación.
Emigna

@Fatalize Esto es fantástico, estaba esperando que alguien usara un lenguaje de prólogo para hacer esto. ¿Lo has probado con el estuche 25x25? Ya
ingresé

@gowrath Voy a ejecutar esto en mi computadora esta tarde, veremos qué pasa.
Fatalize

@Fatalize Parece que se acabó el tiempo pero puedo estar haciéndolo mal. Tampoco confiaría completamente en mis habilidades de ingreso de datos: D
gowrath

@gowrath Se agota el tiempo en TIO, pero lo ejecutaré en el intérprete sin conexión directamente en mi computadora.
Fatalize

9

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

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 por debajo de 200 bytes, crédito a @Bergi. Muchas gracias a @nimi por ayudar a casi reducir a la mitad el tamaño.

Guau. Casi a la mitad del tamaño ahora, en parte por mí, pero principalmente por @nimi.

La función mágica es (#). Encuentra todas las soluciones de un nonograma dado.

Esto es capaz de resolver todos los casos, pero puede ser súper lento, ya que se trata de su complejidad O(2^(len a * len b)). Un punto de referencia rápido reveló 86 GB asignados para un nonograma 5x5.

Dato curioso: funciona para todos los nonogramas, no solo los cuadrados.


Cómo funciona:

  • a#b: Dadas listas de listas de enteros que representan el número de cuadrados, generan todas las cuadrículas ( map(chunk$length b).mapM id$a>>b>>[[0,1]]) y filtran los resultados para mantener solo los válidos.
  • g: Dado un nonograma potencial, suma las corridas de 1 horizontalmente.

Te refieres a O (2 ^ (len a * len b)), no a O ((len a * len b) ^ 2).
Anders Kaseorg

@AndersKaseorg Derecha. Guarde el millón que accidentalmente implicaba allí. : D
ThreeFx

1
Otros pocos bytes: m(chunk$l b)yreplicate(l$a>>b)
Bergi

@ThreeFx 86GB: O ... Por cierto, ¿podrías explicar brevemente cómo compilar esto? Acabo de empezar a aprender haskell y esto está dando errores con ghc. Quiero probarlo :)
gowrath

1
import Data.Listses suficiente, ya que re-exportaciones de ambos Data.Listy Data.List.Split.
nimi

4

Pyth, 91 72 71 bytes

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

Un programa que toma la entrada de una lista de la forma [size, [horizontal clues], [vertical clues]]donde cada pista es una lista de enteros (las pistas vacías son la lista vacía []), e imprime cada solución, separada por una nueva línea, en forma de una cuadrícula binaria donde 1está sombreada y no 0está sombreada .

Esta es una fuerza bruta, por lo que es más o menos O(2^n^2). Comienza a tomar mucho tiempo para rompecabezas más grandes, pero resolverá cualquier tamaño arbitrario con el tiempo suficiente.

Pruébalo en línea

Cómo funciona

El programa genera todos los diseños posibles al tomar el producto cartesiano repetido [0, 1]con una longitud igual a size^2. Esto se divide en trozos, dando una lista para cada línea horizontal. Cada línea está codificada por longitud de recorrido, filtrada por la presencia 1y aplanada, dejando la pista para esa línea. Esto luego se verifica contra la entrada. El proceso anterior se repite para la transposición de los fragmentos, verificando las líneas verticales. Si hay un hit, cada fragmento se concatena, y los fragmentos concatenados se unen en nuevas líneas y se imprimen implícitamente, con una nueva línea final.

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

Gracias a @ Pietu1998 por algunos consejos


Este puede ser el programa Pyth más largo que he visto
Business Cat

=ZhZes igual a =hZ, y FNes igual a V.
PurkkaKoodari

@TheBikingViking ¿Qué quieres decir exactamente con el tiempo suficiente? Estoy bastante seguro de que esto no resolvería un 25x25 por ahora si lo hubiera comenzado desde la concepción del universo.
Gowrath

1
@gowrath ¡También estoy bastante seguro de eso! Soy nuevo en Pyth, y después del tiempo que esto me llevó, ni siquiera quiero considerar intentar implementar un algoritmo mejor
TheBikingViking

2

Javascript (ES6), 401 386 333 bytes

Este es un intento temprano. No es muy eficiente, pero tenía curiosidad por probar una solución usando expresiones regulares en la representación binaria de las filas y columnas.

Por ejemplo, traducirá la pista [3,1]a la siguiente expresión regular:

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

En este momento, esta versión no tiene en cuenta las pistas cuadradas. Probablemente agregaré esto más tarde.

Código

(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)}

Salida

La solución se muestra en formato binario. Como:

00110
01110
11100
11101
00001

Prueba

Esta es una prueba simple en la cuadrícula de ejemplo.

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]]
));


buena idea. Sin embargo, mata mis navegadores en el rompecabezas de Navidad.
Titus

2

Haskell, 109 bytes

Descargo de responsabilidad: esto se deriva de la respuesta de @ ThreeFx . Lo ayudé a descifrar su respuesta, pero parece haber perdido interés en incluir mis últimas mejoras sustanciales, así que las publico como una nueva respuesta.

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

Ejemplo de uso: [[2],[3],[3],[3,1],[1]] # [[2],[3],[4],[2],[2]]-> [[" ## "," ### ","### ","### #"," #"]].

Fuerza bruta. Pruebe todas las combinaciones de y #, divida trozos int de #, cuente las longitudes y compárelo con la entrada.


1

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

Estaba ansioso por construir un incremento de secuencia especializado y probar mi generador cartesiano una vez más;
pero dejó caer el cartesiano a favor de retroceder para resolver el gran rompecabezas más rápido.

$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]));
  • espera sugerencias en matrices $rpara sugerencias de fila, sugerencias $cde columna y $ssugerencias cuadradas.
  • tira invalid argument supplied for foreachsi no encuentra solución.
  • Para obtener el recuento de bytes correcto, use un físico \ny elimine los otros dos saltos de línea.

descripción

1) a partir de sugerencias de fila,
genere posibles filas que satisfagan las sugerencias cuadradas
y recuerde sus recuentos para cada índice de fila.

2) retroceda sobre las combinaciones de filas:
si la combinación satisface los consejos de la columna, busque una combinación
más profunda o devuelta, o intente la siguiente posibilidad para esta fila

3) solución de impresión


El último golf tuvo un impacto severo en el rendimiento;
pero eliminé las asignaciones de perfiles para los puntos de referencia finales.

Reemplace $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++;
para deshacer el último paso de golf.

ejemplos

Para el pequeño ejemplo ( 17 a 21 alrededor de 12 8 7 6.7 5.3 ms), use

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

para el rompecabezas de navidad:

  • mató a mi pequeño servidor doméstico con la solución anterior
  • mató al navegador con salidas de prueba
  • ahora resuelto en 50 37.8 45.5 alrededor de 36 segundos

coloque los datos de la pregunta en un archivo christmas.nonogramy use este código para importar:

$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;}

Descompostura

$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
El gran ejemplo mata a mi pequeño servidor doméstico (500 - Error interno del servidor). Las combinaciones están listas después de 15 segundos, pero el producto cartesiano tiene 1.823E + 61 miembros. (Las filas 7 y 22 solo tienen una solución por cierto). Se debe mejorar el algoritmo.
Titus

Creo que esto podría acelerarse si usas el retroceso recursivo. Sin embargo, ¡gran trabajo!
Gowrath

@gowrath: el retroceso da un poco e incluso ahorra bytes ... el número entero con aritmética de bits da aproximadamente un 50% de velocidad, pero aumenta el tamaño (tengo que averiguar aún cuánto cuesta exactamente) ... Todavía estoy en ello.
Titus

@gowrath: perseguí mi error; estaba en el incremento (¿dónde más?): $dtiene que estar en el orden correcto paraforeach
Titus
Al usar nuestro sitio, usted reconoce que ha leído y comprende nuestra Política de Cookies y Política de Privacidad.
Licensed under cc by-sa 3.0 with attribution required.