Судоку — популярная головоломка, основной задачей которой является размещение цифр в правильном порядке.
Игровое поле представляет собой квадрат 9х9 клеток. Клетки сгруппированы в девять сегментов 3х3. В каждой клетке может быть записана цифра от одного до девяти. Основным правилом судоку является то, что цифра не может повторяться в строке, столбце и сегменте.
Под катом приводится алгоритм решения судоку, реализованный на JavaScript. Рассмотрены только базовые тактики решения головоломки, но этого достаточно для большого числа судоку легкого и среднего уровня.
Ячейка на поле может иметь одно из трех состояний:
Поиски решения заканчиваются, если после перебора всех нерешенных ячеек ни для одной из них не удалось найти решение или не было изменено количество кандидатов:
Есть много способов решения судоку, но в целях демонстрации рассмотрим два наиболее простых метода: «Одиночка» и «Скрытый одиночка». Это базовые методы решения судоку и они отлично подходят для изучения основных принципов.
Метод «Одиночка» основывается непосредственно на правилах судоку: любая цифра может встречаться только один раз в строке колонке и сегменте. С точки зрения алгоритма это означает, что из списка кандидатов ячейки будут вычеркиваться все найденные числа из ее строки, столбца и сегмента.
Метод «Скрытый одиночка» использует правило: если среди кандидатов в строке/столбце/сегменте отдельная цифра встречается только в одной ячейке, то значение этой ячейки и будет равняться такой цифре.
Весь код оформлен в виде класса Sudoku. Для решения головоломки необходимо создать экземпляр класса, передать в него условие задачи и вызвать метод solve(). Результат решения выводится методом html(), который возвращает отформатированный таблицу и количество проходов, которое потребовалось для поиска решения. Если решение не может быть найдено, то будет выведена таблица найденных результатов и по наведению курсора на пустые ячейки будет показываться список кандидатов.
Исходные значения задаются целочисленной матрицей 9х9. В ячейках записывается «0», если значение не определено и число иначе.
Результат работы алгоритма выглядит так:
Исходный код класса Sudoku, а также несколько примеров задач доступны по этой ссылке.
Судоку [Википедия]
Как решать судоку: способы, методы и стратегия
UPD Пользователь rozboris любезно создал демку
UPD 2 Реализован обратный поиск. Теперь решает все судоку. И новая демка от rozboris.
UPD3 Так как ссылки в статье умерли, выложил исходники на Google Code.
Игровое поле представляет собой квадрат 9х9 клеток. Клетки сгруппированы в девять сегментов 3х3. В каждой клетке может быть записана цифра от одного до девяти. Основным правилом судоку является то, что цифра не может повторяться в строке, столбце и сегменте.
Под катом приводится алгоритм решения судоку, реализованный на JavaScript. Рассмотрены только базовые тактики решения головоломки, но этого достаточно для большого числа судоку легкого и среднего уровня.
Основная идея
Ячейка на поле может иметь одно из трех состояний:
- in — значение определено изначально
- unknown — значение не определено
- solved — значение найдено
Поиски решения заканчиваются, если после перебора всех нерешенных ячеек ни для одной из них не удалось найти решение или не было изменено количество кандидатов:
/**
* Решение судоку
*
* Метод в цикле пытается решить судоку, если на текущем этапе не изменилось
* ни одного элемента, то решение прекращается.
*/
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 method updateSuggests()
Реализация алгоритма
Есть много способов решения судоку, но в целях демонстрации рассмотрим два наиболее простых метода: «Одиночка» и «Скрытый одиночка». Это базовые методы решения судоку и они отлично подходят для изучения основных принципов.
Метод «Одиночка» основывается непосредственно на правилах судоку: любая цифра может встречаться только один раз в строке колонке и сегменте. С точки зрения алгоритма это означает, что из списка кандидатов ячейки будут вычеркиваться все найденные числа из ее строки, столбца и сегмента.
/**
* Метод "Одиночка"
*/
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()
Метод «Скрытый одиночка» использует правило: если среди кандидатов в строке/столбце/сегменте отдельная цифра встречается только в одной ячейке, то значение этой ячейки и будет равняться такой цифре.
/**
* Метод "Скрытый одиночка"
*/
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», если значение не определено и число иначе.
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());
Результат работы алгоритма выглядит так:
Исходный код класса Sudoku, а также несколько примеров задач доступны по этой ссылке.
Ссылки
Судоку [Википедия]
Как решать судоку: способы, методы и стратегия
UPD Пользователь rozboris любезно создал демку
UPD 2 Реализован обратный поиск. Теперь решает все судоку. И новая демка от rozboris.
UPD3 Так как ссылки в статье умерли, выложил исходники на Google Code.