Piadas ruins à parte, eu gosto de Sudoku e naturalmente veio a questão de como resolver um destes problemas, de forma eficiente, no computador. Fiquei ainda mais interessado depois que vi na Wikipedia que uma das soluções usa uma ideia do grande Donald Knuth.
Para o caso (improvável) de alguém não conhecer este problema, trata-se de completar um diagrama de 9 por 9 posições, obedecendo as seguintes regras:
- Em cada casa deve ser colocado um dígito de 1 a 9
- Não pode ocorrer repetição de dígito na linha
- Não pode ocorrer repetição de dígito na coluna
- Não pode ocorrer repetição de dígito nos blocos (sub diagramas 3x3 em destaque).
Neste primeiro post, vamos ver a solução mais óbvia, a força bruta. Sendo mais técnico, vamos usar backtracking (ou depth-first). Repetidamente escolhemos a próxima opção para a próxima opção não preenchida, até o problema ser resolvido ou encontrarmos um conflito. Se encontramos um conflito, voltamos atrás na escolha anterior (backtrack). A wikipedia tem uma animação bonitinho de como isto funciona.
Nesta primeira experiência eu usei C#. As principais questões a serem resolvidas foram:
- Como controlar quais opções podem ser testadas em cada posição. Controlei isto associando um mapa de bits em cada posição.
- Quais posições já estão preenchidas e com qual dígito. Para isto basta guardar o dígito ou zero (indicando não preenchida) para cada posição.
- Como voltar à posição anterior. Fui radical neste ponto, guardando uma imagem completa do diagrama.
- A classe Sudoku representa o diagrama.
- Cada posição é armazenada em um inteiro de 16 bits, onde 9 bits controlam os dígitos permitidos e 4 bits guardam o dígito atual (ou zero se posição não preenchida).
- O método Mark registra a escrita de um dígito em uma posição, atualizando os mapas de bits de todas as posições na mesma linha, coluna e bloco.
- O método Next procura a próxima posição não preenchida e NextVal escreve o próximo valor nesta posição.
- O método Solve procura a solução, copiando o diagrama atual, colocando o próximo valor na próxima posição não preenchida e procurando a solução neste novo diagrama.
- O método Load lê um problema de um arquivo texto.
Nenhum comentário:
Postar um comentário