33

用回溯算法求解数独问题

 4 years ago
source link: http://mp.weixin.qq.com/s?__biz=MzI3NzIzMDY0NA%3D%3D&%3Bmid=2247493684&%3Bidx=1&%3Bsn=4c169cb1451c1a93dc2b37bf1158b1e3
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.
neoserver,ios ssh client
// 每日前端夜话 第408篇
// 正文共:1200 字
// 预计阅读时间:5 分钟
3qMnMnQ.png!mobile

前几天我们在《浅析常见的算法范式》中讨论了一些常见的算法范式,但是还留下了回溯算法没有解决。本文来研究回溯算法。

回溯是通过逐步构建解决方案来解决递归问题的算法。通常回溯从可能的解决方案开始,如果它不起作用,则需要回溯并尝试另一种解决方案,直到找到可行的解决方案为止。回溯在解决 CSP(约束满足问题)时特别有用,例如填字游戏、口算题和数独等。

通常回溯算法可用于以下三种类型的问题:

  1. 需要找到可行解决方案的决策问题

  2. 需要找到最佳解决方案的优化问题

  3. 需要找到一组可行解决方案的列举问题

在本文中,我将通过解决数独问题来演示回溯策略。

解决数独问题

针对此类问题的回溯算法会尝试在每个空格中列举所有的数字,直到问题被解决为止。先从 main 方法开始:

function sudokuSolver(matrix) {
if (solveSudoku(matrix) === true) {
return matrix;
}
return '无解';
}

接下来是算法的主要逻辑:

const UNASSIGNED = 0;

function solveSudoku(matrix) {
let row = 0;
let col = 0;
let checkBlankSpaces = false;

// 验证数独是否已解决,如果尚未解决,则获取下一个空格的位置
for (row = 0; row < matrix.length; row++) {
for (col = 0; col < matrix[row].length; col++) {
if (matrix[row][col] === UNASSIGNED) {
checkBlankSpaces = true;
break;
}
}
if (checkBlankSpaces === true) {
break;
}
}
//当没有空格时则意味着已经解决
if (checkBlankSpaces === false) {
return true;
}

// 尝试用正确的数字填充空格
for (let num = 1; num <= 9; num++) {
// isSafe 用于检查在行、列或 3x3 的格子中是否已经存在了数字 num(代码实现在后面)
if (isSafe(matrix, row, col, num)) {
matrix[row][col] = num;

if (solveSudoku(matrix)) {
return true;
}
// 如果 num 所在的位置不合适,需要再次标记为“空格”,然后用不同的 num 回溯
matrix[row][col] = UNASSIGNED;
}
}
return false;
}

接下来看辅助函数的实现:

function isSafe(matrix, row, col, num) {
return (
!usedInRow(matrix, row, num) &&
!usedInCol(matrix, col, num) &&
!usedInBox(matrix, row - (row % 3), col - (col % 3), num)
);
}

function usedInRow(matrix, row, num) {
for (let col = 0; col < matrix.length; col++) {
if (matrix[row][col] === num) {
return true;
}
}
return false;
}

function usedInCol(matrix, col, num) {
for (let row = 0; row < matrix.length; row++) {
if (matrix[row][col] === num) {
return true;
}
}
return false;
}

function usedInBox(matrix, boxStartRow, boxStartCol, num) {
for (let row = 0; row < 3; row++) {
for (let col = 0; col < 3; col++) {
if (matrix[row + boxStartRow][col + boxStartCol] === num) {
return true;
}
}
}
return false;
}

最后对算法进行测试:

const sudokuGrid = [
[5, 3, 0, 0, 7, 0, 0, 0, 0],
[6, 0, 0, 1, 9, 5, 0, 0, 0],
[0, 9, 8, 0, 0, 0, 0, 6, 0],
[8, 0, 0, 0, 6, 0, 0, 0, 3],
[4, 0, 0, 8, 0, 3, 0, 0, 1],
[7, 0, 0, 0, 2, 0, 0, 0, 6],
[0, 6, 0, 0, 0, 0, 2, 8, 0],
[0, 0, 0, 4, 1, 9, 0, 0, 5],
[0, 0, 0, 0, 8, 0, 0, 7, 9]
];

console.log(sudokuSolver(sudokuGrid));

以下是通过回溯法求解数独问题的模拟动画:

a267Br3.gif!mobile通过回溯法解决数独问题

希望本文能帮你理解回溯算法,以后我门还会再讨论更多有趣的算法。

rYNvUzy.gif!mobilezyyeqeA.png!mobile精彩文章回顾,点击直达
VR7zQf.jpg!mobile

前端先锋

长按扫描维码

关注我们 获取更多前端资讯

|最新技术|业界动态|

|学习视频|源码资源|

前端先锋

R3AbQzJ.gif!mobile

转了吗

V7JBzuN.gif!mobile

赞了吗

2qUriuu.gif!mobile

在看吗


Recommend

About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK