Игровое поле представляет собой квадрат 9х9 клеток. Клетки сгруппированы в девять сегментов 3х3. В каждой клетке может быть записана цифра от одного до девяти. Основным правилом судоку является то, что цифра не может повторяться в строке, столбце и сегменте.
Под катом приводится алгоритм решения судоку, реализованный на JavaScript. Рассмотрены только базовые тактики решения головоломки, но этого достаточно для большого числа судоку легкого и среднего уровня. И даже для некоторых сложных.
Основная идея
Ячейка на поле может иметь одно из трех состояний:- in — значение определено изначально
- unknown — значение не определено
- solved — значение найдено
Поиски решения заканчиваются, если после перебора всех нерешенных ячеек ни для одной из них не удалось найти решение или не было изменено количество кандидатов:
* This source code was highlighted with Source Code Highlighter.
- /**
- * Решение судоку
- *
- * Метод в цикле пытается решить судоку, если на текущем этапе не изменилось
- * ни одного элемента, то решение прекращается.
- */
- function solve() {
- var changed = 0;
- do {
- // сужаем множество значений для всех нерешенных чисел
- changed = updateSuggests();
- steps++;
- if ( 81 < steps ) {
- // Зашита от цикла
- break;
- }
- } while (changed);
- }; // end of method solve()
- /**
- * Обновляем множество предположений
- *
- * Проверяем основные правила -- уникальность в строке, столбце и секции.
- */
- function updateSuggests() {
- var changed = 0;
- var buf = arrayDiff(solved[1][3][2], rowContent(1));
- buf = arrayDiff(buf, colContent(3));
- buf = arrayDiff(buf, sectContent(1, 3));
- for ( var i=0; i<9; i++) {
- for ( var j=0; j<9; j++) {
- if ( 'unknown' != solved[i][j][1] ) {
- // Здесь решение либо найдено, либо задано
- continue;
- }
- // "Одиночка"
- changed += solveSingle(i, j);
- // "Скрытый одиночка"
- changed += solveHiddenSingle(i, j);
- }
- }
- return changed;
- }; // end of methos updateSuggests()
Реализация алгоритма
Есть много способов решения судоку, но в целях демонстрации рассмотрим два наиболее простых метода: «Одиночка» и «Скрытый одиночка». Это базовые методы решения судоку и они отлично подходят для изучения основных принципов.Метод «Одиночка» основывается непосредственно на правилах судоку: любая цифра может встречаться только один раз в строке колонке и сегменте. С точки зрения алгоритма это означает, что из списка кандидатов ячейки будут вычеркиваться все найденные числа из ее строки, столбца и сегмента.
Метод «Скрытый одиночка» использует правило: если среди кандидатов в строке/столбце/сегменте отдельная цифра встречается только в одной ячейке, то значение этой ячейки и будет равняться такой цифре.
* This source code was highlighted with Source Code Highlighter.
- /**
- * Метод "Одиночка"
- */
- function solveSingle(i, j) {
- solved[i][j][2] = arrayDiff(solved[i][j][2], rowContent(i));
- solved[i][j][2] = arrayDiff(solved[i][j][2], colContent(j));
- solved[i][j][2] = arrayDiff(solved[i][j][2], sectContent(i, j));
- if ( 1 == solved[i][j][2].length ) {
- // Исключили все варианты кроме одного
- markSolved(i, j, solved[i][j][2][0]);
- return 1;
- }
- return 0;
- }; // end of method solveSingle()
* This source code was highlighted with Source Code Highlighter.
- /**
- * Метод "Скрытый одиночка"
- */
- function solveHiddenSingle(i, j) {
- var less_suggest = lessRowSuggest(i, j);
- var changed = 0;
- if ( 1 == less_suggest.length ) {
- markSolved(i, j, less_suggest[0]);
- changed++;
- }
- var less_suggest = lessColSuggest(i, j);
- if ( 1 == less_suggest.length ) {
- markSolved(i, j, less_suggest[0]);
- changed++;
- }
- var less_suggest = lessSectSuggest(i, j);
- if ( 1 == less_suggest.length ) {
- markSolved(i, j, less_suggest[0]);
- changed++;
- }
- return changed;
- }; // end of method solveHiddenSingle()
- /**
- * Минимизированное множество предположений по строке
- */
- function lessRowSuggest(i, j) {
- var less_suggest = solved[i][j][2];
- for ( var k=0; k<9; k++ ) {
- if ( k == j || 'unknown' != solved[i][k][1] ) {
- continue;
- }
- less_suggest = arrayDiff(less_suggest, solved[i][k][2]);
- }
- return less_suggest;
- }; // end of method lessRowSuggest()
- /**
- * Минимизированное множество предположений по столбцу
- */
- function lessColSuggest(i, j) {
- var less_suggest = solved[i][j][2];
- for ( var k=0; k<9; k++ ) {
- if ( k == i || 'unknown' != solved[k][j][1] ) {
- continue;
- }
- less_suggest = arrayDiff(less_suggest, solved[k][j][2]);
- }
- return less_suggest;
- }; // end of method lessColSuggest()
- /**
- * Минимизированное множество предположений по секции
- */
- function lessSectSuggest(i, j) {
- var less_suggest = solved[i][j][2];
- var offset = sectOffset(i, j);
- for ( var k=0; k<3; k++ ) {
- for ( var l=0; l<3; l++ ) {
- if ( ((offset.i+k) == i && (offset.j+l) == j)|| 'unknown' != solved[offset.i+k][offset.j+l][1] ) {
- continue;
- }
- less_suggest = arrayDiff(less_suggest, solved[offset.i+k][offset.j+l][2]);
- }
- }
- return less_suggest;
- }; // end of method lessSectSuggest()
Использование
Весь код оформлен в виде класса Sudoku. Для решения головоломки необходимо создать экземпляр класса, передать в него условие задачи и вызвать метод solve(). Результат решения выводится методом html(), который возвращает отформатированный таблицу и количество проходов, которое потребовалось для поиска решения. Если решение не может быть найдено, то будет выведена таблица найденных результатов и по наведению курсора на пустые ячейки будет показываться список кандидатов.Исходные значения задаются целочисленной матрицей 9х9. В ячейках записывается "0", если значение не определено и число иначе.
Результат работы алгоритма выглядит так:
* This source code was highlighted with Source Code Highlighter.
- var in_val = [
- [0, 0, 3, 0, 0, 8, 2, 0, 4],
- [0, 2, 0, 0, 6, 4, 0, 1, 0],
- [9, 0, 0, 0, 0, 0, 0, 0, 8],
- [0, 8, 0, 0, 0, 0, 0, 0, 0],
- [0, 0, 0, 0, 0, 6, 9, 8, 0],
- [0, 0, 0, 0, 0, 0, 5, 0, 0],
- [0, 0, 4, 9, 0, 7, 0, 3, 0],
- [8, 0, 0, 0, 0, 1, 0, 0, 0],
- [0, 7, 0, 0, 5, 0, 4, 0, 0]
- ];
- var sudoku = new Sudoku(in_val);
- document.write(sudoku.html());
Комментариев нет:
Отправить комментарий