Pouvez-vous battre le British Intelligence? (Solveur Nonogram)

20

Il est temps de se lancer dans une quête périlleuse pour vaincre le renseignement britannique. Le but de ce défi est d'écrire le code le plus court qui résoudra un nonogramme.

Qu'est-ce qu'un nonogramme?

Puzzle Nonogram

Les règles sont simples. Vous avez une grille de carrés qui doivent être remplis en noir ou laissés en blanc. À côté de chaque ligne de la grille sont répertoriées les longueurs des séries de carrés noirs sur cette ligne. Au-dessus de chaque colonne sont répertoriées les longueurs des séries de carrés noirs dans cette colonne. Votre objectif est de trouver tous les carrés noirs. Dans ce type de casse-tête, les nombres sont une forme de tomographie discrète qui mesure le nombre de lignes ininterrompues de carrés remplis dans une ligne ou une colonne donnée. Par exemple, un indice de "4 8 3" signifierait qu'il y a des ensembles de quatre, huit et trois carrés remplis, dans cet ordre, avec au moins un carré vide entre les groupes successifs. [ 1 ] [ 2 ]

La solution au nonogramme ci-dessus serait donc:

Nonogramme résolu

Détails d'implémentation

Vous pouvez choisir de représenter le Nonogramme comme vous le souhaitez et de le prendre comme entrée de la manière que vous jugerez appropriée pour votre langue. Il en va de même pour la sortie. Le but de ce défi est littéralement de simplement faire le travail; si vous pouvez résoudre le non-programme avec n'importe quelle sortie de votre programme, c'est valable. Une mise en garde est que vous ne pouvez pas utiliser un solveur en ligne :)

Ce problème est très complexe sur le plan algorithmique (np-complet) en ce qu'il n'y a pas de solution complètement efficace et en tant que tel, vous ne serez pas pénalisé pour ne pas pouvoir en résoudre de plus grandes, bien que votre réponse sera fortement récompensée si elle est capable de gérer de gros cas (voir bonus). En tant que référence, ma solution fonctionne jusqu'à environ 25x25 en 5 à 10 secondes. Pour permettre la flexibilité entre les différentes langues, les solutions qui prennent moins de 5 minutes pour un nonogramme 25x25 sont assez bonnes.

Vous pouvez supposer un puzzle dans toujours un nonogramme NxN carré.

Vous pouvez utiliser ce fabricant de puzzles en ligne sans programme pour tester vos solutions.

Notation

Bien sûr, vous êtes libre d'utiliser la langue de votre choix et comme il s'agit de code golf, les entrées seront triées dans l'ordre: accuracy -> length of code -> speed.Cependant, ne vous découragez pas par les langues de golf de code, les réponses dans toutes les langues qui montrent des tentatives de golf d'une manière intéressante sera voté!

Prime

J'ai en fait entendu parler des Nonograms grâce à une carte de Noël cryptographique publiée par le British Intelligence ici . La première partie était essentiellement un nonogramme massif de 25x25. Si votre solution est capable de résoudre ce problème, vous obtiendrez des félicitations :)

Pour vous faciliter la vie en termes de saisie de données, j'ai fourni comment j'ai représenté les données de ce puzzle spécifique pour votre utilisation gratuite. Les 25 premières lignes sont les indices de ligne, suivis d'une ligne de séparation `` - '', suivis de 25 lignes des indices de colonne, suivis d'une ligne de séparation `` # '', puis d'une représentation de la grille avec les indices carrés remplis.

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

Et voici une version légèrement différente pour votre commodité; un tuple séparé par des virgules (ligne, col) où chaque élément est une liste de listes.

([[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]])
gowrath
la source
Malheureusement, mon site Web est en panne, mais il avait un solveur Nonogram assez rapide; 5 à 10 minutes semblent excessifs.
Neil
1
@dwana Vous n'avez pas à vous soucier des cas insolubles. Quant à la réponse aléatoire, sur un nonogramme 25x25, vous avez 2 ^ 625 configurations possibles. Dans le contexte, c'est plus du double du nombre d'atomes dans l'univers connu (c'est-à-dire si vous avez utilisé chaque atome de l'univers comme un peu, vous n'auriez toujours pas assez d'espace pour stocker les possibilités). En termes de temps, si cela vous prenait une nano seconde (généreuse) pour vérifier la validité de chaque configuration, il faudrait 7 vies de l'univers pour que le code finisse de s'exécuter :)
gowrath
1
Ty pour clarifier les cas insolubles. (+ j'ai un PC magique qui valide une réponse en ~ 2.1546362E-186 secondes)
dwana
1
Votre CSV n'a pas d'indications carrées. Voici quelques JS pour les générer: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

Réponses:

5

Brachylog , 70 69 octets

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

Cela prend une liste de deux listes (d'abord les indicateurs de lignes, puis ceux de colonne). Chaque indicateur est lui-même une liste (pour les situations comme [3,1]sur une ligne).

Cette version prend environ 3 minutes pour résoudre l'exemple 5 par 5 du défi.

Version beaucoup plus efficace, 91 octets

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

Essayez-le en ligne!

Celui-ci n'est pas une force brute complète: la seule différence est que celui-ci impose des contraintes sur les valeurs des cellules de sorte que le nombre de 1 dans chaque ligne et colonne corresponde aux nombres donnés comme indicateurs en entrée. La seule partie de force brute est alors de trouver la grille unique avec les contraintes pour lesquelles les "blocs" de 1 correspondent à ce qui est donné à titre indicatif.

Celui-ci prend environ 0,05 seconde sur l'exemple 5 par 5 du défi. C'est encore beaucoup trop lent pour le cas bonus, car je ne sais pas comment exprimer les blocs de ceux séparés par un ou plusieurs zéros en termes de contraintes.

Explication

J'expliquerai ci-dessous la version 93 octets. La seule différence entre les deux est l'appel au prédicat 3 qui n'existe pas dans la version 70 octets, et la numérotation des prédicats (car il y en a un de moins).

  • Prédicat 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
    
  • Prédicat 1: force les lignes à avoir une longueur spécifique et à ce que chaque cellule soit 0 ou 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)
    
  • Prédicat 2: contraindre une variable à 0 ou 1

    .<2,      Input = Output < 2
    _1<       Output > -1
    
  • Prédicat 3: La somme de 1 dans une liste doit être égale à la somme des indicateurs (par exemple, si l'indicateur est [3: 1] alors la liste doit avoir la somme 4)

    :+a       Sum the elements of the list and sum the indicator
    #=,       Both sums must be equal
    ?h        Output is the list
    
  • Prédicat 4: Vérifiez que les blocs de 1 correspondent à l'indicateur

    @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)
    
  • Prédicat 5: Vrai pour les blocs de 1, faux sinon

    e.        Output is an element of the input
      h1,     Its first value is 1
    
Fatalize
la source
Se sent comme l'outil parfait pour le travail. Dans l'attente de l'explication.
Emigna
@Fatalize C'est fantastique, j'attendais que quelqu'un utilise un langage prologue pour le faire. L'avez-vous essayé avec le boîtier 25x25? Je suis entré dans les données pour vous déjà
gowrath
@gowrath Je l'exécuterai sur mon ordinateur cet après-midi, nous verrons ce qui se passe.
Fatalize
@Fatalize Semble expirer mais je me trompe peut-être. Je ne compterais pas non plus entièrement sur mes compétences en saisie de données: D
gowrath
@gowrath Il expire sur TIO, mais je l'exécuterai sur l'interpréteur hors ligne directement sur mon ordinateur.
Fatalize
9

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

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)

Enfin moins de 200 octets, crédit à @Bergi. Un grand merci à @nimi pour avoir aidé à réduire de moitié la taille.

Sensationnel. Presque à moitié maintenant, en partie à cause de moi mais principalement à cause de @nimi.

La fonction magique est (#). Il trouve toutes les solutions d'un nonogramme donné.

Cela peut résoudre tous les cas, mais peut être super lent, car sa complexité est à peu près O(2^(len a * len b)). Un benchmark rapide a révélé 86 Go alloués pour un nonogramme 5x5.

Fait amusant: il fonctionne pour tous les non-programmes, pas seulement les carrés.


Comment ça fonctionne:

  • a#b: Étant donné des listes de listes d'entiers qui représentent le nombre de carrés, générez toutes les grilles ( map(chunk$length b).mapM id$a>>b>>[[0,1]]) et filtrez les résultats pour ne garder que ceux valides.
  • g: Étant donné un nonogramme potentiel, il additionne les séquences de 1 horizontalement.
ThreeFx
la source
Vous voulez dire O (2 ^ (len a * len b)), pas O ((len a * len b) ^ 2).
Anders Kaseorg
@AndersKaseorg À droite. Gardez le million que j'ai accidentellement laissé entendre. : D
ThreeFx
1
Encore quelques octets: m(chunk$l b)etreplicate(l$a>>b)
Bergi
@ThreeFx 86GB: O ... Btw pourriez-vous expliquer brièvement comment compiler cela? Je viens juste de commencer à apprendre haskell et cela donne des erreurs avec ghc.
Je
1
import Data.Listsest suffisant, car il réexporte à la fois Data.Listet Data.List.Split.
nimi
4

Pyth, 91 72 71 octets

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

Un programme qui accepte une entrée d'une liste de la forme dans [size, [horizontal clues], [vertical clues]]laquelle chaque indice est une liste de nombres entiers (indices vides sont la liste vide, []), et imprime chaque solution, nouvelle ligne séparée, sous la forme d'une grille binaire où 1est ombrée et 0est non ombrée .

Il s'agit d'une force brute, tout comme l'est en gros O(2^n^2). Cela commence à prendre beaucoup de temps pour les énigmes plus grandes, mais résoudra toute taille arbitraire avec un temps suffisant.

Essayez-le en ligne

Comment ça fonctionne

Le programme génère toutes les dispositions possibles en prenant le produit cartésien répété de [0, 1]avec une longueur égale à size^2. Ceci est ensuite divisé en morceaux, donnant une liste pour chaque ligne horizontale. Chaque ligne est codée en longueur, filtrée par la présence de 1et aplatie, laissant l'indice de cette ligne. Ceci est ensuite vérifié par rapport à l'entrée. Le processus ci-dessus est répété pour la transposition des morceaux, en vérifiant les lignes verticales. S'il y a un hit, chaque morceau est concaténé, et les morceaux concaténés sont joints sur des retours à la ligne et implicitement imprimés, avec un retour à la ligne de fin.

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

Merci à @ Pietu1998 pour quelques conseils

TheBikingViking
la source
C'est peut-être le programme Pyth le plus long que j'ai jamais vu
Business Cat
=ZhZest égal à =hZet FNest égal à V.
PurkkaKoodari
@TheBikingViking Que voulez-vous dire exactement avec suffisamment de temps? Je suis presque sûr que cela ne résoudrait pas un 25x25 maintenant si vous l'aviez commencé à partir de la conception de l'univers.
gowrath
1
@gowrath J'en suis aussi assez sûr! Je suis nouveau sur Pyth, et après le temps que cela m'a pris, je ne veux même pas envisager d'essayer de mettre en œuvre un meilleur algorithme
TheBikingViking
2

Javascript (ES6), 401 386 333 octets

Il s'agit d'une première tentative. Ce n'est pas très efficace mais j'étais curieux de tester une solution en utilisant des expressions régulières sur la représentation binaire des lignes et des colonnes.

Par exemple, il traduira l'indice [3,1]en l'expression régulière suivante:

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

Pour l'instant, cette version ne tient pas compte des indices carrés. J'ajouterai probablement cela plus tard.

Code

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

Production

La solution est affichée au format binaire. Tel que:

00110
01110
11100
11101
00001

Tester

Il s'agit d'un test simple sur l'exemple de grille.

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

Arnauld
la source
bonne idée. tue mes navigateurs sur le puzzle de Noël, cependant.
Titus
2

Haskell, 109 octets

Avertissement: ceci est dérivé de la réponse de @ ThreeFx . Je l'ai aidé à comprendre sa réponse mais il semble avoir perdu tout intérêt à inclure mes dernières améliorations substantielles, alors je les poste comme nouvelle réponse.

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

Exemple d'utilisation: [[2],[3],[3],[3,1],[1]] # [[2],[3],[4],[2],[2]]-> [[" ## "," ### ","### ","### #"," #"]].

Force brute. Essayez toutes les combinaisons de et #, séparez les morceaux de #, comptez les longueurs et comparez-les à l'entrée.

nimi
la source
1

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

J'étais impatient de construire un incrément de séquence spécialisé et d'essayer à nouveau mon générateur cartésien;
mais a abandonné le cartésien en faveur du retour en arrière pour résoudre le grand puzzle plus rapidement.

$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]));
  • attend des conseils dans les tableaux $rpour les conseils de ligne, $cpour les conseils de colonne et $spour les conseils carrés.
  • lance invalid argument supplied for foreachs'il ne trouve pas de solution.
  • pour obtenir le nombre d'octets correct, utilisez un physique \net supprimez les deux autres sauts de ligne.

la description

1) à partir des indications de ligne,
générez des lignes possibles qui satisfont les indications de carré
et rappelez-vous leur nombre pour chaque index de ligne.

2) revenir en arrière sur les combinaisons de lignes:
si la combinaison satisfait les indications de la colonne, recherchez une combinaison plus profonde ou réussie,
sinon essayez la possibilité suivante pour cette ligne

3) solution d'impression


Le dernier golf a eu un impact sévère sur la performance;
mais j'ai supprimé les affectations de profilage pour les repères finaux.

Remplacez $n=$n*($f=($p[$y][$i[$y]]>>$x&1)==$v)+$k=$f?:($v=!$v)||$n==$h[$z++];
par if(($p[$y][$i[$y]]>>$x&1)-$v){$k=($v=!$v)||$n==$h[$z++];$n=1;}else$n++;
pour annuler la dernière étape de golf.

exemples

Pour le petit exemple ( 17 à 21 vers 12 8 7 6,7 5,3 ms), utilisez

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

pour le puzzle de noël:

  • tué mon petit serveur domestique avec l'ancienne solution
  • tué le navigateur avec des sorties de test
  • maintenant résolu en 50 37,8 45,5 environ 36 secondes

mettre les données de la question dans un fichier christmas.nonogramet utiliser ce code pour importer:

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

panne

$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]));
Titus
la source
1
Le grand exemple tue mon petit serveur domestique (500 - Erreur de serveur interne). Les combinaisons sont prêtes après 15 secondes, mais le produit cartésien compte 1,823E + 61 membres. (Les 7e et 22e rangées n'ont qu'une seule solution entre les deux.) L'algorithme doit être amélioré.
Titus
Je pense que cela pourrait être accéléré si vous utilisez un retour arrière récursif. Néanmoins, excellent travail!
gowrath
@gowrath: le retour en arrière donne un peu et économise même des octets ... l'entier avec des arithmétiques de bits donne environ 50% de vitesse mais augmente la taille (je dois encore savoir combien cela coûte exactement) ... Je suis toujours là-dessus.
Titus
@gowrath: J'ai chassé mon bug; il était dans l'incrément (où d'autre?): $ddoit être dans le bon ordre pourforeach
Titus