Alors que j'écrivais les mots croisés japonais en 4 heures

Je regarde paresseusement la liste des cours dispensés par des écoliers récemment dressés par des collÚgues de Sirius ... Alors, qu'est-ce que c'est? «Rechercher des objets combinatoires à l'aide de solveurs SAT»? "Nous avons créé un solveur sudoku, des mots croisés japonais et plus?"

L'idée surgit que les problÚmes NP exhaustifs sont réductibles les uns aux autres, et en particulier, sont réductibles à la recherche de la faisabilité d'une formule booléenne . Un des auteurs de Habr a exprimé cette idée ici , et franchement, pour moi, c'est comme de la magie.

Bien sûr, en tant que personne qui a suivi un cours de mathématiques discrÚtes et de complexité des algorithmes, je sais théoriquement que les tùches sont réductibles les unes aux autres. Mais il est généralement difficile de le faire, et je ferais mieux de croire sur parole les professeurs et les autres personnes intelligentes.

Mais alors il est proposĂ© aux ... ECOLIERS! À l'intĂ©rieur, le poinçon a commencĂ© Ă  susciter de la p ... crĂ©ativitĂ© et a dĂ©clarĂ©: "Eh bien, ce n'est probablement pas difficile de le fixer, une fois que les Ă©tudiants sont proposĂ©s. Je ne peux pas le comprendre ?? Écoutez, ils promettent d'utiliser la bibliothĂšque Python, mais je connais gĂ©nĂ©ralement le python ... »

Et il était environ 21 heures, ce qui a quelque peu émoussé mon regard critique sur la complexité du problÚme ... (en fait, d'autres chroniques de programmation de 4 heures)

21:02 La premiĂšre Ă©tape de la quĂȘte - j'essaie d'installer la bibliothĂšque en Python. Par expĂ©rience avec les rĂ©seaux de neurones, je sais dĂ©jĂ  qu'en fait, le code prend parfois moins de temps que la configuration des paquets.

L'utilisation du py installateur de pip classique n'est pas installé, il produit une erreur dans l'esprit de Microsoft Visual C ++ 14.0 est requis .

Nous montons Ă  la recherche de la source du problĂšme, nous arrivons Ă  Stackoverflow , oĂč nous trouvons un petit lien vers les outils de construction Visual C ++ 2015 requis , y compris apparemment le compilateur C ++.

Téléchargez, essayez d'installer ... wow, il faut 4 Go! En fait, je n'ai besoin que d'un compilateur, mais bon.

Pendant que cette chose est téléchargée et installée, nous suivons les liens dans le programme publié par Sirius ... Oui, pas beaucoup - des exemples de projets Python, et une liste des tùches possibles pour le cours. Pas de présentations, pas de vidéos pour comprendre rapidement comment travailler avec la bibliothÚque.

D'accord, commençons par analyser les exemples. Nous allons à queens_pycosat.py ... Wow, en cours de route, c'est la solution au problÚme familier de placer des reines sur un échiquier (la tùche est d'organiser 8 reines afin qu'elles ne se battent pas).

21: 10-21: 30 Nous comprenons et essayons de commencer.

La logique gĂ©nĂ©rale commence Ă  ĂȘtre tracĂ©e:
SAT- () (1, 2, 3),(-1, 2 ..), (True/False).

, , .

( , ); , , 8x8 = 64 , .

(1 2 3
 8) — 1 . — [1,2,3...8]
— (9
 16) ..

— 1 !
[ 1 2] ( — ). [-1,-2],[-1,-3] .

, , . , .

( ) — (1 2
 8) ( 1 2) (...). , — .

Oui, je n'Ă©tais pas si intelligent tout de suite, maintenant que je l'ai compris ...

En général, nous nous copions le code d'enseignement. Pour vous assurer que nous comprenons quelque chose, désactivez la vérification des diagonales - puis la tùche se transforme en une tùche sur les tours.

On commence, on reçoit: ça marche! 21:35 Ayant un peu familiarisé avec la documentation de la bibliothÚque, nous découvrons qu'il y a des défis pour trouver une solution ou toutes les solutions. Dites-moi, mon cher ami, combien y a-t-il d'arrangements de tour sur une carte 8x8? Trouverez-vous tout? La machine a réfléchi pendant un moment, et j'ai commencé à google la table factorielle. Une minute plus tard, le solveur a répondu - "40320 solutions ont été trouvées." Vérifiez - vraiment 8 !, Tout est correct.

| | | | | | | |x|
| | | | | | |x| |
| | | | | |x| | |
| | | | |x| | | |
| | | |x| | | | |
| | |x| | | | | |
| |x| | | | | | |
|x| | | | | | | |











J'ai supprimĂ© la condition que les tours ne se touchent pas verticalement ... mais combien sera-t-il maintenant? Doit ĂȘtre 8 ^ 8 = 16777216. Il m'est alors apparu qu'il prendrait beaucoup de temps Ă  compter - nous interrompons le processus.

A Ă©galement trouvĂ© un moyen rapide de dĂ©river 4 solutions sans tout calculer - Ă  partir de tout dans la mĂȘme bibliothĂšque. C'est fait - nous trouvons une solution, aprĂšs quoi nous l'ajoutons aux exceptions de la formule boolĂ©enne ...

Comment déduire plusieurs solutions en python
for cnter in range(4):
  sol = pycosat.solve(clauses)
  if isinstance(sol, list):
    print(sol)
    clauses.append([-x for x in sol]) #   -      
  else: #    - 
    return
#     ,    ,   .


En gĂ©nĂ©ral, nous obtenons quelques solutions supplĂ©mentaires au «problĂšme de la tour»: 21:45 I: En gĂ©nĂ©ral, tout est clair, si quelque chose, maintenant je peux le fixer. Je vais lire un livre et un spa ... DÉBUT CRÉATIF: Eh bien, vous comprenez comment tout fonctionne. Et Ă©crivons un simple petit mot croisĂ© de mots croisĂ©s japonais! Prenons le cas le plus simple - sur chaque ligne et colonne, il n'y aura pas plus d'un chiffre (et, par consĂ©quent, un bloc). Il vous suffit de rechercher parmi les options possibles pour la ligne / colonne, c'est rapide ici, puis de le charger dans le solveur. Faisons-le, hein? Moi: ( sans ressentir d'embuscade ) Eh bien, allez ... il semble que la vĂ©ritĂ© n'est pas pour longtemps ... Pour plus de simplicitĂ©, nous ne prenons que des mots croisĂ©s carrĂ©s NxN - en mĂȘme temps, vous pouvez prendre le code de sortie prĂȘt et penser oĂč les horizontales et verticales ne sont pas nĂ©cessaires ...

| | | | | | | |x|
| | | | | | |x| |
| | | | | |x| | |
| | | | |x| | | |
| | | |x| | | | |
| |x| | | | | | |
| | |x| | | | | |
|x| | | | | | | |











22:10 Cela ne fonctionne pas: ((
Nous essayons le puzzle de mots croisĂ©s 3x3, au format 1-0-1 (jusqu'Ă  prĂ©sent, seuls les blocs sont horizontaux, nous ne prenons pas en compte les verticales). Il donne toutes les cellules vides, l'infection, ne prend mĂȘme pas en compte les blocs remplis ...

D'accord, en détail Nous explorons le format de transfert des données vers le solveur, puis quelque chose, pour le dire en douceur, l'inattendu se révÚle

, «-» ?

/ /, — . , .

[1] 3 , :
[X__],[_X_],[__X]
, ,
[1,-2,-3],[-1,2,-3],[-1,-2,3]

, 

:
[1 -2 -3] [-1, 2...] [...]

, . , . — , !

Embuscade. En fait, il y a deux options - soit pour trouver les conditions d'un puzzle de mots croisés dans KNF, soit pour trouver comment convertir DNF en KNF (eh bien, quelqu'un aurait dû y penser, d'autant plus que les deux sont des formules booléennes!)

22:50 J'ai terminé la collation. Je n'ai pas pensé à comment créer des conditions pour KNF (plus tard, j'ai trouvé qu'elles étaient décrites dans l' article , mais c'était long pour les programmer, et, franchement, antisportif).

Nous recherchons donc un convertisseur de DNF en CNF. Les descriptions formelles des transformations dans le style académique sont dispersées sur Internet ... pourquoi en ai-je besoin? Je ne vais définitivement pas m'en tenir à eux maintenant, je résoudrais le problÚme. Ainsi, nous voyons la seule bibliothÚque (!) Python avec le nom et les fonctions appropriés. Maintenant, nous allons nous connecter ...

23:10Cela fonctionne drĂŽlement - il crĂ©e des variables supplĂ©mentaires, et Ă  travers elles, il apporte DNF Ă  CNF. Apparemment, faire honnĂȘtement KNF est assez difficile ...

Donc, ça ne marche pas non plus! Quel est le problÚme?
Nous soumettons l'entrée de test de DNF: lstall = [[1,2,3]]. Autrement dit, nous nous attendons à ce que seulement [1,2,3] (correspond à la ligne complÚte [XXX]) soit une solution valide. La

bibliothĂšque donne KNF: cnf = [[-1, -2, -3, 4], [1 , -4], [2, -4], [3, -4]]
Ce n'est pas clair, mais trÚs intéressant. Eh bien, disons que je vous crois ...

Nous demandons Ă  pycosat de trouver toutes les solutions pour cnf:
[1, 2, 3, 4]
[1, 2, -3, -4]
[1, -2, -3, -4]
[1 , -2, 3, -4]
[-1, -2, -3, -4]
[-1, -2, 3, -4]
[-1, 2, -3, -4]
[-1, 2, 3, -4]

Au lieu de celui attendu, il y en a huit! Que faire?

, , 4 True. ?

: cnf2 = [[-1, -2, -3, 4], [1, -4], [2, -4], [3, -4], [4]]
pycosat cnf2:
1: [1, 2, 3, 4]

, , ! , , .
— !

23:10 Elle dĂ©cide! Voici un mot croisĂ© 1-3-1 verticalement et horizontalement: Voici une double solution d'un mot croisĂ© 1-0-1 verticalement et horizontalement: Moi: Donc, tout fonctionne pour moi, j'ai terminĂ©. Wow, il est dĂ©jĂ  23 heures. Spa ... DÉBUT CRÉATIF: Comprenez-vous qu'il vous reste trĂšs peu pour rĂ©soudre les mots croisĂ©s japonais? Il suffit d'Ă©crire une procĂ©dure rĂ©cursive qui donnera toutes les options de ligne non pas pour un bloc, mais pour N ... Moi: Eh bien ... cela semble ĂȘtre facile, oui ... 00:00 À minuit, le tupit de tĂȘte ne cuisine dĂ©jĂ  pas si bien, mais je continue de dĂ©boguer rĂ©cursivitĂ©. Termes du mot croisĂ© japonais - entre les blocs, il doit y avoir au moins un espace, mais il peut y en avoir plus d'un.

| |x| |
|x|x|x|
| |x| |




1
| | |x|
| | | |
|x| | |
2
|x| | |
| | | |
| | |x|












Donc sous la condition [1,2,1] pour 6 cellules, le programme devrait générer la seule option
[X_XX_X],
et pour 7 cellules il y a déjà 4 options:
[X_XX_X_],
[X_XX__X],
[X__XX_X],
[_X_XX_X],

tout le temps en Je calcule par erreur l'emplacement des cellules une par une, et au diable - c'est plus facile de démarrer et de réparer que de penser ...

00:30 Est-ce que ça marche? Quoi d'autre Ă  vĂ©rifier? Alors, oĂč trouver une description orientĂ©e machine des mots croisĂ©s ... Sur ce site, non, sur ce - non ... D'accord, au diable, j'interromps les stylos. 00:50 Ça marche vraiment O_O Voici un crabe avec jcrosswords.ru/crossword/65

3x3
rows = [[1,1],[0],[1,1]]
cols = [[1,1],[0],[1,1]]

:
|X| |X|
| | | |
|X| |X|

4x4
rows = [[1,1],[0],[0],[1,1]]
cols = [[1],[1],[1],[1]]

:
1
| |X| |X|
| | | | |
| | | | |
|X| |X| |
2
|X| |X| |
| | | | |
| | | | |
| |X| |X|







dĂ©cidĂ© par mon solveur. La taille maximale testĂ©e est de 10 x 10, maintenant je suis trĂšs paresseux pour interrompre plus de mots croisĂ©s dans le programme ... - Moi: Wow, c'est dĂ©jĂ  un matin! Attendez, cela n'a pris que 4 heures Ă  partir du moment oĂč je ne savais rien du solveur SAT? Alors, comment c'Ă©tait? (des notes rapides sont Ă©crites sur ce qui s'est passĂ© ...)

|x|_|x|_|_|_|_|x|_|x|
|x|x|x|_|_|_|_|x|x|x|
|_|x|_|_|_|_|_|_|x|_|
|_|x|_|x|_|_|x|_|x|_|
|_|x|x|x|x|x|x|x|x|_|
|_|_|x|x|x|x|x|x|_|_|
|x|x|x|x|x|x|x|x|x|x|
|_|_|x|x|x|x|x|x|_|_|
|x|x|_|x|x|x|x|_|x|x|
|x|_|_|_|_|_|_|_|_|x|








Épilogue.

Bonjour . Sur Habr, l' article sur un solveur sur Rust se trouve . Le tĂ©lĂ©chargement Ă  partir de mots croisĂ©s ne fonctionne pas, mais aprĂšs ĂȘtre allĂ© Ă  l'article Github spĂ©cifiĂ© dans l'article, nous trouvons un lien cachĂ© pour exporter des mots croisĂ©s dans un format lisible par machine sur le site Web de mots croisĂ©s Webpbn . L'option «Format pour la version 1 du solveur Python de Kyle Keen» nous convient, elle est la mĂȘme que la nĂŽtre. Lorsque vous n'avez pas besoin d'interrompre les mots croisĂ©s avec des stylos, les choses vont plus vite. Il devient rapidement clair que le maximum qu'un solveur est capable de rĂ©soudre est des mots croisĂ©s de l'ordre de 30x30. Le problĂšme principal est la gĂ©nĂ©ration mĂȘme de variantes de lignes individuelles, dans le cas d'un tas de blocs de ces conditions, TRÈS beaucoup et tout commence Ă  ralentir:



1. PremiÚrement, les options de chaßne prennent beaucoup de temps à générer. Générer uniquement toutes les options pour la chaßne [2,1,3,1,4] de longueur 60 prend déjà environ une demi-minute ... (il n'y en a que 2118760, si quelqu'un est intéressé)
2. De plus, la conversion DNF-> CNF crée trop de conditions et de variables supplémentaires ...

Mais s'il y a peu de blocs, mĂȘme les mots croisĂ©s de grande taille peuvent ĂȘtre rĂ©solus en un temps significatif.

Chat - tout va bien
20x20
51511 clauses...
2851 last var number
--- 0.05186057090759277 seconds for clauses gen ---
1
| | |X|X| | | | | | | | | | | | | | | | |
| |X|X| | | | | | | | | | | | | | | | | |
| |X| | | | | | | | | | | | | | | | | | |
| |X| | | | | | | | | | | | | | | | | | |
| |X| | | | | |X|X|X| | | | | | | | | | |
|X|X| | | | |X|X|X|X|X| | | | | | | | | |
|X| | | | |X|X|X|X|X|X|X| | | |X| | | |X|
|X| | | | |X|X|X|X|X|X|X|X| | |X|X| |X|X|
|X| | | |X|X|X|X|X|X|X|X|X| | |X|X|X|X|X|
|X|X| | |X|X|X|X|X|X|X|X|X|X|X|X|X|X|X|X|
| |X| |X|X|X|X|X|X|X|X|X|X|X|X|X|X|X|X|X|
| |X|X|X|X|X|X|X| |X|X|X|X|X|X|X|X|X|X|X|
| | |X|X|X|X|X| | | |X|X|X|X|X| |X|X|X| |
| | |X|X|X|X|X| | | |X|X|X|X| | | | | | |
| | | |X|X|X| | | | | |X|X|X| | | | | | |
| | | |X|X| | | | | | |X|X| | | | | | | |
| | |X|X| | | | | | | | |X| | | | | | | |
| | |X| | | | | | | | | |X| | | | | | | |
| | |X|X| | | | | | | | |X|X| | | | | | |
| | |X|X| | | | | | | | |X|X| | | | | | |
--- 0.02073383331298828 seconds for solve ---


Dans un désir de tester plus de mots croisés, j'ai rapidement fait l'alignement - tout mot croisé est réduit à un carré lorsque nous remplissons les lignes restantes avec des zéros. Oui, ce n'est pas optimal, mais maintenant j'hésite à refaire le code ...

Cheval - 30x38
22
13622799 clauses... -
350737 last var number
--- 14.18206787109375 seconds for clauses gen ---
1
| | | | | | | | | | | | | | | | | | | | | | | | | | | | | | |X|X|X| | | | | |
| | | | | | | | | | | | | | | | | | | | | | | | | | | |X|X|X|X|X|X|X| | | | |
| | | | | | | | | | | | | | | | | | | | | | | | | | | | |X|X|X|X|X|X|X| | | |
| | | | | | | | | | | | | | | | | | | | | | | | | |X|X|X|X|X|X|X|X| |X|X| | |
| | | | | | | | | | | | | | | | | | | | | | | | | | |X|X|X|X|X|X|X|X|X|X|X| |
| | | | | | | | | | | | | | | | | | | | | | | |X|X|X|X|X|X|X|X|X|X|X|X|X|X|X|
| | | | | | | | | | | | | | | | | | | | | | | | |X|X|X|X|X|X|X|X| |X|X|X|X|X|
| | | | | | | | | | | | | | | | | | | | | | | | |X|X|X|X|X|X|X| | | | | |X| |
| | | | | | | | | | |X|X|X|X| | | | | | | | | |X|X|X|X|X|X|X|X| | | | | | | |
| | | | | | | | |X|X|X|X|X|X|X|X|X| | | |X|X|X|X|X|X|X|X|X|X| | | | | | | | |
| | | | | | |X|X|X|X|X|X|X|X|X|X|X|X|X|X|X|X|X|X|X|X|X|X|X|X| | | | | | | | |
| | | | | |X|X|X|X|X|X|X|X|X|X|X|X|X|X|X|X|X|X|X|X|X|X|X|X| | | | | | | | | |
| | | | |X|X|X| |X|X|X|X|X|X|X|X|X|X|X|X|X|X|X|X|X|X|X|X|X| | | | | | | | | |
| | | |X|X|X| | |X|X|X|X|X|X|X|X|X|X|X|X|X|X|X|X|X|X|X|X|X| | | | | | | | | |
| | |X|X|X| | | |X|X|X|X|X|X|X|X|X|X|X|X|X|X|X|X|X|X|X|X|X|X|X|X|X|X| | | | |
| |X|X|X| | | |X|X|X|X|X|X|X|X|X|X|X|X|X|X|X|X|X|X|X|X|X|X|X|X|X|X|X|X| | | |
|X|X|X|X| | | |X|X|X|X|X|X|X|X|X|X|X|X|X|X|X|X|X|X|X|X|X|X|X| | | |X|X| | | |
| |X|X| | | |X|X|X|X|X| | | | |X|X|X|X|X|X|X|X|X|X|X|X| | | | | | |X|X| | | |
| | |X| | | |X|X|X|X| |X| | | | | | |X|X|X|X|X|X| | | |X| | | | | |X|X| | | |
| | | | | |X|X|X|X| |X|X| | | | | | | | | | | | | |X|X|X| | | | | |X|X| | | |
| | | | |X|X|X|X| |X|X| | | | | | | | | | | | | | |X|X|X| | | | | |X|X| | | |
| | | | |X|X|X| | |X|X| | | | | | | | | | | | | | | |X|X| | | | | |X|X| | | |
| | | | |X|X| | | |X|X| | | | | | | | | | | | | | | |X|X| | | | |X|X|X| | | |
| | | |X|X|X| | | | |X|X| | | | | | | | | | | | | | |X|X| | | | |X|X| | | | |
| | | |X|X| | | | | |X|X| | | | | | | | | | | | | | |X|X| | | | |X| | | | | |
| | | |X|X| | | | | |X|X| | | | | | | | | | | | | | |X|X| | | | | | | | | | |
| | | |X|X| | | | | | |X|X| | | | | | | | | | | | | |X|X| | | | | | | | | | |
| | | |X|X| | | | | | |X|X| | | | | | | | | | | | | |X|X| | | | | | | | | | |
| | | | |X|X| | | | | | |X|X| | | | | | | | | | | | | |X|X| | | | | | | | | |
| | | | |X|X|X| | | | | |X|X|X| | | | | | | | | | | | |X|X|X| | | | | | | | |

--- 17.535892963409424 seconds for solve ---



Dans la publication originale sur le solveur SAT , les conditions et les variables sont gĂ©nĂ©rĂ©es plus honnĂȘtement - par consĂ©quent, la solution est plus rapide et prend probablement moins de mĂ©moire. Pour dĂ©cider d'un cheval, cela me prend environ 2,7 Go de RAM, et cela prend des choses compliquĂ©es pour les 12 installĂ©s, puis ils commencent Ă  Ă©changer frĂ©nĂ©tiquement sur le disque ...

Plus de conclusions:

1. J'ai compris pourquoi les exemples de pycosat les plus rapidement googlés sont des solutions Sudoku. Les rÚgles de Sudoku sont beaucoup plus faciles à mettre sur le solveur, car vous n'avez pas besoin de décrire la relation complexe entre les blocs et les cellules, essentiellement les conditions "chaque chiffre dans chaque ligne" et "seulement un chiffre dans une ligne".
Mais j'aime beaucoup moins le sudoku, donc je ne regrette pas la décision que j'ai choisie.

2. J'ai aimé comment mélanger DNF et CNF. Si vous ne voulez pas écrire les bonnes conditions - ne le faites pas; générez simplement toutes les options. Bien sûr, ce n'est pas optimal, mais cela fonctionne.

Bien sûr, je n'ai pas établi de records pour la vitesse de résolution des mots croisés, mais j'ai passé un bon moment. La chose la plus importante est que je peux le faire! :) Ce que je te souhaite aussi!

All Articles