数独
数独(Sudoku)是一种数学逻辑游戏,游戏由9×9个格子组成,玩家需要根据格子提供的数字推理出其他格子的数字。游戏设计者会提供最少17个数字使得解答谜题只有一个答案。数独的图示表示其实也是一个邻接矩阵。
标准数独是由一个给与了提示数字的9x9网格组成,每行、列、宫各自都要填上1-9的数字,要做到每行、列、宫里的数字都不重复。宫是由3×3的小格子组成的。
数独基础解法:
• 唯一余数法: 点算某格的 [等位群格位] 中已经出现过哪些数,如果已经出现1 – 9中的8格,那么这格就是第9个数,此数被称为唯一余数。
• 排除法:就是利用数独中行、列和宫内不能填入相同数字的规则,利用已出现的数字对同行、同列和同宫内其他格进行排斥相同数字的方法。
📐 数学公式
-
📌 JavaScript
参考代码:index.js
/** * 索引 => 行列坐标 * @param {Number} index - 索引值 * @returns */ function i2rc(index) { return { row: Math.floor(index / 9), col: index % 9 }; } /** * 行列坐标 => 索引 * @param {Number} row - 行数 * @param {Number} col - 列数 * @returns */ function rc2i(row, col) { return row * 9 + col; } /** * 有哪些可用选项,即在给定单元格中可以接受哪些数字(也将它们称为“移动”) * @param {Array} board - 一个数组,它就是数独的面板,使用数组矩阵表示 * @param {Number} index - 索引 * @returns */ function getChoices(board, index) { let choices = []; for (let value = 1; value <= 9; ++value) { if (acceptable(board, index, value)) { choices.push(value); } } return choices; } /** * 棋盘是我们的数组,包含81个元素,此函数将返回当前在该索引的单元格上允许的数字数组。 * 拼图的实际规则是在函数acceptable()中实现的:我们可以使用未在同一行、同一列或同一 3x3 正方形中使用的数字 * @param {Array} board - 一个数组,它就是数独的面板,使用数组矩阵表示 * @param {Number} index - 索引值 * @param {Number} value - 1~9中的某个值 * @returns */ function acceptable(board, index, value) { let { row, col } = i2rc(index); /* 列上已经存在,则不可接受 */ for (let r = 0; r < 9; ++r) if (board[rc2i(r, col)] == value) return false; /* 行上已经存在,则不可接受 */ for (let c = 0; c < 9; ++c) if (board[rc2i(row, c)] == value) return false; /* 如果已经出现在同一个 3x3 方格中,也不能接受 */ let r1 = Math.floor(row / 3) * 3; let c1 = Math.floor(col / 3) * 3; for (let r = r1; r < r1 + 3; ++r) { for (let c = c1; c < c1 + 3; ++c) { if (board[rc2i(r, c)] == value) return false; } } return true; } /** * 回溯检查问题是否解决 * @param {Array} board - 一个数组,它就是数独的面板,使用数组矩阵表示 * @returns */ function solve(board) { /* 遍历每个索引 */ /* 再次为每个字段寻找填充和计算移动的最佳位置 */ /* ----------------- */ let index, moves; for (let i = 0; i < board.length; ++i) { if (!board[i]) { let m = getChoices(board, i); moves = m; index = i; if (m.length === 0) break; } } /* 回溯检查问题是否解决 */ /* ----------------- */ if (index == null) return true; for (let m of moves) { /* 尝试一种选择 */ board[index] = m; /* 如果我们能解决下一个单元格 */ if (solve(board)) return true; } /* 没有任何动作;失败并清除单元格 */ board[index] = 0; /* 回溯 */ return false; } /** * 数独的解题器 * @param {Array} matrix - 一个数组,它就是数独的面板,使用数组矩阵表示 * @returns */ export function sudokuSolver(matrix) { if (solve(matrix) === true) { return matrix; } return '不能找到解决方案'; }
测试:test.js
import { sudokuSolver } from './index'; const myBoard = [ 0, 0, 0, 0, 0, 0, 0, 0, 6, 0, 3, 0, 0, 7, 1, 0, 4, 0, 0, 0, 0, 0, 0, 0, 8, 0, 0, 0, 0, 0, 9, 0, 8, 0, 7, 1, 1, 0, 3, 0, 0, 0, 0, 0, 0, 0, 0, 2, 0, 3, 0, 9, 0, 0, 5, 0, 7, 0, 0, 6, 0, 0, 0, 2, 0, 0, 0, 0, 0, 7, 0, 0, 0, 0, 1, 8, 0, 0, 0, 0, 2, ]; console.log( sudokuSolver(myBoard) ); /* 结果: [ 7, 2, 4, 3, 8, 9, 1, 5, 6, 6, 3, 8, 5, 7, 1, 2, 4, 9, 9, 1, 5, 6, 4, 2, 8, 3, 7, 4, 5, 6, 9, 2, 8, 3, 7, 1, 1, 9, 3, 7, 6, 4, 5, 2, 8, 8, 7, 2, 1, 3, 5, 9, 6, 4, 5, 8, 7, 2, 9, 6, 4, 1, 3, 2, 6, 9, 4, 1, 3, 7, 8, 5, 3, 4, 1, 8, 5, 7, 6, 9, 2 ] */