Como eu escrevi as palavras cruzadas japonesas em 4 horas

Olho preguiçosamente a lista de cursos ministrados por alunos recentemente apresentados por colegas de Sirius ... Então, o que é isso? “Procura objetos combinatórios usando solucionadores SAT”? "Fizemos um solucionador de sudoku, palavras cruzadas em japonês e muito mais?"

Surge o pensamento de que os problemas exaustivos de PN são redutíveis um ao outro e, em particular, são redutíveis à busca da viabilidade de uma fórmula booleana . Um dos autores de Habr expressou essa idéia aqui e, francamente, para mim é como mágica.

Obviamente, como uma pessoa que concluiu um curso de matemática discreta e a complexidade de algoritmos, eu teoricamente sei que as tarefas são redutíveis uma à outra. Mas geralmente é difícil fazer isso, e é melhor aceitar minha palavra pelos professores e outras pessoas inteligentes.

Mas então é oferecido a ... ESCOLAS! dentro, o furador começou a despertar p ... criatividade e disse: “Bem, provavelmente não é difícil prendê-lo, uma vez que os alunos são oferecidos. Não consigo descobrir? Olha, eles prometem usar a biblioteca Python, mas eu geralmente conheço o python ... ”

E eram cerca de 21 horas, o que entorpeceu meu olhar crítico sobre a complexidade do problema ... (na verdade, outras crônicas da programação de 4 horas)

21:02 A primeira etapa da missão - estou tentando instalar a biblioteca em Python. Por experiência com redes neurais, eu já sei que, de fato, o código às vezes leva menos tempo do que a configuração de pacotes.

Usando a instalação clássica do pip, o pycosat não está instalado, pois produz um erro no espírito do Microsoft Visual C ++ 14.0 .

Escalamos para procurar a origem do problema, chegamos ao Stackoverflow , onde encontramos um pequeno link para as Ferramentas de Construção Visual C ++ 2015 necessárias , aparentemente incluindo o compilador C ++.

Baixe, tentando instalar ... uau, requer 4 GB! Na verdade, eu só preciso de um compilador, mas tudo bem.

Enquanto isso está sendo baixado e instalado, seguimos os links do programa postado pelo Sirius ... Sim, não muito - exemplos de projetos em Python, mas uma lista de tarefas possíveis para o curso. Sem apresentações, sem vídeos para entender rapidamente como trabalhar com a biblioteca.

Ok, vamos começar analisando os exemplos. Vamos para queens_pycosat.py ... Uau, ao longo do caminho, essa é a solução para o problema familiar de colocar rainhas em um tabuleiro de xadrez (a tarefa é organizar 8 rainhas para que não se superem).

21: 10-21: 30 Entendemos e tentamos começar.

A lógica geral começa a ser rastreada:
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) (...). , — .

Sim, eu não era tão inteligente agora, agora que descobri ...

Em geral, copiamos o código de ensino para nós mesmos. Para ter certeza de que entendemos algo, desative a verificação de diagonais - a tarefa se transformará em uma tarefa sobre gralhas.

Começamos, recebemos: Funciona! 21:35 Depois de nos familiarizarmos com a documentação da biblioteca, descobrimos que há desafios para encontrar uma solução ou todas as soluções. Diga-me, meu caro amigo, quantos arranjos de torres existem em um tabuleiro 8x8? Você vai encontrar tudo? A máquina pensou por um tempo e comecei a pesquisar na tabela fatorial. Um minuto depois, o solucionador respondeu: "40.320 soluções foram encontradas". Cheque - realmente 8 !, Tudo está correto.

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











Eu removi a condição de que as gralhas não se chocassem verticalmente ... mas quanto será agora? Deve ser 8 ^ 8 = 16777216. Então me dei conta de que ele levaria muito tempo para contar - interrompemos o processo.

Também foi encontrada uma maneira rápida de derivar 4 soluções sem calcular tudo - de tudo na mesma biblioteca. Isso é feito - encontramos uma solução, após a qual a adicionamos às exceções da fórmula booleana ...

Como deduzir várias soluções em 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
#     ,    ,   .


Em geral, temos mais algumas soluções para o "problema da torre": 21:45 I: Em geral, tudo está claro, se é que existe alguma coisa, agora eu posso fixá-lo. Vou ler um livro e um spa ... INÍCIO CRIATIVO: Bem, você entende como tudo funciona. E vamos escrever um pequeno quebra-cabeça simples de palavras cruzadas em japonês! Considere o caso mais simples - em cada linha e coluna não haverá mais de um dígito (e, consequentemente, um bloco). Você simplesmente pesquisa as opções possíveis da linha / coluna, é rápido aqui e, em seguida, carrega-o no solucionador. Vamos fazer isso, né? Eu: ( sem sentir uma emboscada ) Bem, vamos lá ... parece que a verdade não é longa ... Por uma questão de simplicidade, usamos apenas palavras cruzadas quadradas NxN - ao mesmo tempo, você pode preparar o código de saída e pensar onde a horizontal e a vertical não são necessárias ...

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











22:10 Não funciona: ((
tentamos as palavras cruzadas 3x3, no formato 1-0-1 (até agora apenas os blocos são horizontais, não levamos em consideração as verticais)). Dá todas as células vazias, infecção, nem leva em conta os blocos preenchidos ...

Ok, em detalhes Nós nos aprofundamos no formato de transferência de dados para o solucionador e, em seguida, algo, para dizer o mínimo, é inesperado ...

, «-» ?

/ /, — . , .

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

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

, . , . — , !

Emboscada. Na verdade, existem duas opções - criar as condições para um jogo de palavras cruzadas no KNF ou como converter o DNF em KNF (bem, alguém deveria ter pensado nisso, especialmente porque as duas são fórmulas booleanas!)

22:50 Eu terminei o lanche. Não pensei em como criar condições para o KNF (depois descobri que elas foram descritas no artigo , mas demorou muito tempo para programá-las e, francamente, antidesportivas).

Então, nós estamos procurando um conversor de DNF para CNF. Descrições formais de transformações no estilo acadêmico estão espalhadas na Internet ... por que eu preciso delas? Definitivamente não vou ficar com eles agora, resolveria o problema. Portanto, vemos a única biblioteca Python (!) Com o nome e as funções apropriados. Agora vamos conectar ...

23:10É engraçado - cria variáveis ​​adicionais e, através delas, leva o DNF ao CNF. Aparentemente, fazer KNF honestamente é bastante difícil ...

Então, também não funciona! Qual é o problema?
Nós enviamos a entrada de teste do DNF: lstall = [[1,2,3]]. Ou seja, esperamos que somente [1,2,3] (corresponde à linha completa [XXX]) seja uma solução válida. A

biblioteca fornece KNF: cnf = [[-1, -2, -3, 4], [1 , -4], [2, -4], [3, -4]]
Não está claro, mas é muito interessante. Bem, digamos que eu acredito em você ...

Pedimos ao pycosat para encontrar todas as soluções para 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]

Em vez do esperado, há oito deles! O que fazer?

, , 4 True. ?

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

, , ! , , .
— !

23:10 Ela decide! Aqui estão as palavras cruzadas 1-3-1 na vertical e na horizontal: Aqui está uma solução dupla de palavras cruzadas 1-0-1 na vertical e na horizontal: Eu: Então, tudo funciona para mim, eu terminei. Uau, já são 23:00 Spa ... INÍCIO CRIATIVO: Você entende que resta muito pouco para resolver palavras cruzadas em japonês? Só é necessário escrever um procedimento recursivo que fornecerá todas as opções de linha, não para um bloco, mas para N ... Eu: Bem ... parece fácil, sim ... 00:00 À meia-noite, o tupit principal já cozinha não tão bem, mas continuo depurando recursão. Termos das palavras cruzadas em japonês - entre os blocos, deve haver pelo menos um espaço, mas pode haver mais de um.

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




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












Portanto, sob a condição [1,2,1] para 6 células, o programa deve gerar a única opção
[X_XX_X]
e para 7 células já existem 4 opções:
[X_XX_X_],
[X_XX__X],
[X__XX_X],
[_X_XX_X],

o tempo todo em Estou calculando erroneamente a localização das células por uma e, para o inferno - é mais fácil começar e consertar do que pensar ...

00:30 Funciona ? O que mais para verificar? Então, onde obter uma descrição de palavras cruzadas orientada para a máquina ... Neste site, não, sobre isso - não ... Ok, para o inferno, vou interromper as canetas. 00:50 Realmente funciona O_O Aqui está um caranguejo com 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|







decidido pelo meu solucionador. O tamanho máximo testado é de 10 x 10, agora é muito preguiçoso interromper mais palavras cruzadas no programa ... - Eu: Uau, é uma da manhã! Espere, demorou apenas 4 horas desde o momento em que eu não sabia nada sobre o SAT-solver. Então, como foi? (notas rápidas são escritas sobre o que aconteceu ...)

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








Posfácio.

Bom dia . Em Habr, encontra-se o artigo sobre um solucionador de ferrugem . O download de palavras cruzadas não funciona, mas depois de acessar o artigo do Github especificado no artigo, encontramos um link oculto para exportar palavras cruzadas em um formato legível por máquina no site Webpbn de palavras cruzadas . A opção “Formato para a versão 1 do solucionador Python de Kyle Keen” é adequada para nós, é a mesma que a nossa. Quando você não precisa interromper palavras cruzadas com canetas, as coisas acontecem mais rapidamente. Logo fica claro que o máximo que um solucionador é capaz de resolver são palavras cruzadas na região de 30x30. O principal problema é a própria geração de variantes de linhas individuais, no caso de um monte de blocos dessas condições, MUITO e tudo começa a desacelerar:



1. Primeiro, as opções de sequência demoram muito tempo para serem geradas. A geração de apenas todas as opções para a cadeia [2,1,3,1,4] de comprimento 60 já leva cerca de meio minuto ... (existem 2118760, se alguém estiver interessado)
2. Além disso, a conversão DNF-> CNF cria muitas condições e variáveis ​​adicionais ...

Mas se houver poucos blocos, mesmo palavras cruzadas grandes poderão ser resolvidas em um momento significativo.

Gato - está tudo bem
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 ---


No desejo de testar mais palavras cruzadas, rapidamente fiz o alinhamento - qualquer palavra cruzada é reduzida a um quadrado quando preenchemos as linhas restantes com zeros. Sim, não é o ideal, mas agora reluto em refazer o código ...

Cavalo - 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 ---



Na publicação original sobre o SAT-solver , condições e variáveis ​​são geradas com mais honestidade - portanto, a solução é mais rápida e provavelmente requer menos memória. Ao decidir um cavalo, são necessários cerca de 2,7 GB de RAM e são necessárias algumas coisas complicadas para todos os 12 instalados, e então eles começam a trocar freneticamente para o disco ...

Mais das conclusões:

1. Percebi por que os exemplos de pycosat mais rapidamente pesquisados ​​são as soluções de Sudoku. As regras do Sudoku são muito mais fáceis de colocar no solucionador, porque você não precisa descrever o relacionamento complexo entre blocos e células, basicamente apenas as condições "cada dígito em cada linha" e "apenas um dígito em uma linha".
Mas eu amo muito menos o sudoku, então não me arrependo da decisão que escolhi.

2. Gostei de como você pode misturar DNF e CNF. Se você não quiser escrever as condições certas, não gere todas as opções. Obviamente, isso não é o ideal, mas funciona.

É claro que não estabeleci registros para a velocidade de resolução de palavras cruzadas, mas me diverti bastante. O mais importante é que eu consigo! :) O que eu desejo a você também!

All Articles