Un nonogramme est un jeu de puzzle japonais dans lequel le but est de dessiner une image en noir et blanc selon une liste de régions contiguës, comme ceci:
Définissez la grandeur nonographique d'une ligne ou d'une colonne comme étant le nombre de régions noires contiguës dans cette ligne ou cette colonne. Par exemple, la ligne supérieure a une magnitude nonographique de 1, car il y a une région de 2 carrés dans cette ligne. La 8e rangée a une magnitude nonographique de 3 car elle en a 2, 2, 1.
Une ligne ou une colonne vide a une magnitude nonographique de 0.
Votre tâche consiste à écrire un programme qui prend une grille de solution pour un nonogramme, et produit une grille de solution avec aussi peu de carrés remplis que possible où chaque ligne et colonne a le même magnutide nonographique que la grille de solution donnée.
Par exemple, une grille non graphique avec tous les carrés remplis a une magnitude nonographique de 1 sur chaque ligne ou colonne:
La même grandeur nonographique pourrait être obtenue simplement en ayant une bande diagonale à travers la grille, ce qui réduit considérablement le nombre de carrés remplis:
Votre programme recevra une entrée composée de 50 000 lignes de ce fichier ( fichier texte tar.gz de 1,32 Mo; 2,15 Mo décompressé), chacune représentant une seule grille de solution de nonogrammes 16 × 16 avec des carrés remplis de manière aléatoire (noir à 80%), et produire encore 50 000 lignes, chacune contenant la grille de solution optimisée pour la grille d'entrée correspondante.
Chaque grille est représentée sous la forme d'une chaîne base64 avec 43 caractères (encodage des carrés de gauche à droite, puis de haut en bas), et votre programme devra renvoyer sa sortie dans le même format. Par exemple, la première grille du fichier est E/lu/+7/f/3rp//f799xn/9//2mv//nvj/bt/yc9/40=
, et s'affiche comme suit:
La grille commence par la E
correspondance avec 000100
, donc les six premières cellules de la rangée supérieure sont toutes blanches sauf la quatrième. Le caractère suivant est celui /
qui correspond 111111
, donc les 6 cellules suivantes sont toutes noires - et ainsi de suite.
Votre programme doit en fait renvoyer une grille de solution avec les amplitudes nonographiques correctes pour chacun des 50 000 cas de test. Il est permis de renvoyer la même grille que l'entrée si rien de mieux n'a été trouvé.
Le programme qui renvoie le moins de carrés remplis au total (dans n'importe quelle langue) est le gagnant, le code plus court étant le bris d'égalité.
Tableau de bord actuel:
- 3,637,260 - Sleafar, Java
- 7270894 - flawr, Matlab
- 10,239,288 - Joe Z., Bash
la source
Réponses:
Python 2 et PuLP - 2 644 688 carrés (minimisés de manière optimale); 10 753 553 carrés (optimisé au maximum)
Golf minimalement à 1152 octets
(NB: les lignes fortement en retrait commencent par des tabulations, pas des espaces.)
Exemple de sortie: https://drive.google.com/file/d/0B-0NVE9E8UJiX3IyQkJZVk82Vkk/view?usp=sharing
Il s'avère que des problèmes comme ceux-ci sont facilement convertibles en programmes linéaires entiers, et j'avais besoin d'un problème de base pour apprendre à utiliser PuLP - une interface python pour une variété de solveurs LP - pour mon propre projet. Il s'avère également que PuLP est extrêmement facile à utiliser, et le générateur de LP non golfé a parfaitement fonctionné la première fois que je l'ai essayé.
Les deux bonnes choses à propos de l'utilisation d'un solveur IP de branche et de liaison pour faire le travail difficile de résoudre ce problème pour moi (à part le fait de ne pas avoir à implémenter un solveur de branche et lié) sont que
Comment utiliser ce programme
Tout d'abord, vous devrez installer PuLP.
pip install pulp
devrait faire l'affaire si vous avez installé pip.Ensuite, vous devrez mettre les éléments suivants dans un fichier appelé "c": https://drive.google.com/file/d/0B-0NVE9E8UJiNFdmYlk1aV9aYzQ/view?usp=sharing
Ensuite, exécutez ce programme dans n'importe quelle version Python 2 tardive du même répertoire. En moins d'une journée, vous aurez un fichier appelé "s" qui contient 50 000 grilles de nonogrammes résolues (dans un format lisible), chacune avec le nombre total de carrés remplis répertoriés en dessous.
Si vous souhaitez plutôt maximiser le nombre de carrés remplis, remplacez la
LpMinimize
ligne 8 par à laLpMaximize
place. Vous obtiendrez une sortie très semblable à celle-ci: https://drive.google.com/file/d/0B-0NVE9E8UJiYjJ2bzlvZ0RXcUU/view?usp=sharingFormat d'entrée
Ce programme utilise un format d'entrée modifié, car Joe Z. a déclaré que nous serions autorisés à ré-encoder le format d'entrée si nous le souhaitons dans un commentaire sur l'OP. Cliquez sur le lien ci-dessus pour voir à quoi il ressemble. Il se compose de 10 000 lignes, chacune contenant 16 chiffres. Les lignes numérotées paires sont les magnitudes des lignes d'une instance donnée, tandis que les lignes numérotées impaires sont les magnitudes des colonnes de la même instance que la ligne au-dessus d'elles. Ce fichier a été généré par le programme suivant:
(Ce programme de ré-encodage m'a également donné une opportunité supplémentaire de tester ma classe BitQueue personnalisée que j'ai créée pour le même projet mentionné ci-dessus. être sauté soit un bit soit un octet à la fois. Dans ce cas, cela a parfaitement fonctionné.)
J'ai ré-encodé l'entrée pour la raison spécifique que pour construire un ILP, les informations supplémentaires sur les grilles qui ont été utilisées pour générer les amplitudes sont parfaitement inutiles. Les grandeurs sont les seules contraintes, et donc les grandeurs sont tout ce à quoi j'avais besoin d'accéder.
Générateur ILP non golfé
Il s'agit du programme qui a réellement produit "l'exemple de sortie" lié ci-dessus. D'où les cordes extra longues à la fin de chaque grille, que j'ai tronquées lors du golf. (La version golfée devrait produire une sortie identique, moins les mots
"Filled squares for "
)Comment ça fonctionne
J'utilise une grille 18x18, la partie centrale 16x16 étant la véritable solution du puzzle.
cells
est cette grille. La première ligne crée 324 variables binaires: "cell_0_0", "cell_0_1", etc. Je crée également des grilles des "espaces" entre et autour des cellules dans la partie solution de la grille.rowseps
pointe sur les 289 variables qui symbolisent les espaces qui séparent les cellules horizontalement, tandis quecolseps
pointe également sur les variables qui marquent les espaces qui séparent les cellules verticalement. Voici un diagramme unicode:Les
0
s et□
s sont les valeurs binaires suivies par lescell
variables, les|
s sont les valeurs binaires suivies par lesrowsep
variables et les-
s sont les valeurs binaires suivies par lescolsep
variables.Telle est la fonction objective. Juste la somme de toutes les
cell
variables. Puisque ce sont des variables binaires, c'est exactement le nombre de carrés remplis dans la solution.Cela met simplement les cellules autour du bord extérieur de la grille à zéro (c'est pourquoi je les ai représentées comme des zéros ci-dessus). Il s'agit du moyen le plus rapide de suivre le nombre de "blocs" de cellules remplis, car il garantit que chaque changement de non rempli à rempli (se déplaçant sur une colonne ou une ligne) correspond à un changement correspondant de rempli à non rempli (et vice versa). ), même si la première ou la dernière cellule de la ligne est remplie. C'est la seule raison d'utiliser une grille 18x18 en premier lieu. Ce n'est pas la seule façon de compter les blocs, mais je pense que c'est la plus simple.
C'est la vraie chair de la logique de l'ILP. Fondamentalement, cela nécessite que chaque cellule (autre que celles de la première ligne et colonne) soit le xor logique de la cellule et le séparateur directement à sa gauche dans sa ligne et directement au-dessus dans sa colonne. J'ai obtenu les contraintes qui simulent un xor dans un programme entier {0,1} à partir de cette merveilleuse réponse: /cs//a/12118/44289
Pour expliquer un peu plus: cette contrainte xor fait que les séparateurs peuvent être 1 si et seulement s'ils se trouvent entre des cellules qui sont 0 et 1 (marquant un changement de non rempli à rempli ou vice versa). Ainsi, il y aura exactement deux fois plus de séparateurs à 1 valeur dans une ligne ou une colonne que le nombre de blocs dans cette ligne ou cette colonne. En d'autres termes, la somme des séparateurs sur une ligne ou une colonne donnée est exactement le double de la magnitude de cette ligne / colonne. D'où les contraintes suivantes:
Et c'est à peu près tout. Le reste demande simplement au solveur par défaut de résoudre l'ILP, puis formate la solution résultante pendant qu'elle l'écrit dans le fichier.
la source
Java,
6.093.0924.332.6563.637.260 carrés (minimisé),10.567.55010.567.69110.568.746 carrés (maximisé)Les deux variantes du programme effectuent à plusieurs reprises des opérations sur la grille source, sans modifier l'amplitude.
Minimizer
rétrécir()
Si un carré noir a 2 voisins blancs et 2 voisins noirs dans un angle de 90 °, il peut être remplacé par un carré blanc.
moveLine ()
Dans les configurations comme ci-dessus, la ligne noire peut être déplacée vers la droite. Cette opération est répétée pour les 4 directions de ligne dans le sens horaire et antihoraire, pour ouvrir de nouvelles possibilités de rétrécissement.
Maximizer
Décommentez la ligne
main()
et commentez la ligne au-dessus pour cette version.grandir()
Si un carré blanc a 2 voisins blancs et 2 voisins noirs dans un angle de 90 °, il peut être remplacé par un carré noir.
moveLine ()
Identique à Minimizer.
La source
la source
Bash - 10 239 288 carrés
Comme solution de référence de dernière place:
Étant donné que votre programme est autorisé à renvoyer la même grille s'il ne peut pas trouver une meilleure solution, l'impression de l'intégralité du fichier textuellement est également valide.
Il y a un total de 10 239 288 carrés noirs dans le fichier de test, ce qui est assez proche des 10 240 000 que vous attendez de 80% des carrés remplis sur 50 000 grilles de 256 carrés chacune. Comme d'habitude avec mes questions sur la batterie de test, j'ai sélectionné le nombre de cas de test en espérant que les scores optimaux seront autour de la plage de 2 millions, bien que je soupçonne que les scores seront plus proches de 4 ou 5 millions cette fois-ci. .
Si quelqu'un peut créer une solution qui maximise les carrés noirs plutôt que de les minimiser et parvient à obtenir plus de 10 240 000, je pourrais envisager de lui donner une prime.
la source
Matlab, 7270894 carrés (~ 71% de l'original)
L'idée est une simple recherche gourmande répétée: pour chaque carré noir, essayez de le mettre au blanc sans changer les grandeurs non géographiques. Répétez cette opération deux fois. (Vous pourriez obtenir de bien meilleurs résultats avec plus de répétitions, mais pas gratuitement: cela se traduit par un temps d'exécution plus long. Maintenant, c'était environ 80 minutes. Je le ferais, si nous n'avions pas à calculer tous les tests 50k ...)
Voici le code (chaque fonction dans un fichier séparé, comme d'habitude.)
la source