Javascript递归回溯数独求解器
Posted
技术标签:
【中文标题】Javascript递归回溯数独求解器【英文标题】:Javascript Recursive Backtracking Sudoku Solver 【发布时间】:2020-02-18 20:13:53 【问题描述】:我最近在 Golang 的帮助下编写了同样的代码。
如果你熟悉 go,你可以在这里看到工作代码。
Go Playground
这是我想要在 python 中完成的任务。 Computerphile Video
我现在正在尝试将其移植到 javascript。
我初始化了游戏状态。
var gameState = [
[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]
]
这是取自数独***页面的网格。
Sudoku Wiki
我编写了以下辅助函数来获取网格上任何位置的行、列和块单元。我对它们都进行了测试,它们似乎都可以正常工作。
function isUnitUnique(unit)
for (let value = 1; value <= 9; value++)
let tally = 0;
for (let index = 0; index < 9; index++)
if (unit[index] == value)
tally++;
if (tally > 1)
return false;
return true;
function getColumnUnit(board, column)
let unit = [];
for (let row = 0; row < 9; row++)
unit.push(board[row][column]);
return unit;
function getBlockUnit(board, x, y)
let unit = []
if (x >= 0 && x <= 2) j = 1;
else if (x >= 3 && x <= 5) j = 4;
else if (x >= 6 && x <= 8) j = 7;
if (y >= 0 && y <= 2) i = 1;
else if (y >= 3 && y <= 5) i = 4;
else if (y >= 6 && y <= 8) i = 7;
unit.push(board[i - 1][j - 1]);
unit.push(board[i - 1][j]);
unit.push(board[i - 1][j + 1]);
unit.push(board[i][j - 1]);
unit.push(board[i][j]);
unit.push(board[i][j + 1]);
unit.push(board[i + 1][j - 1]);
unit.push(board[i + 1][j]);
unit.push(board[i + 1][j + 1]);
return unit;
然后我使用带有以下代码的辅助函数来尝试解决这个难题。这是一种递归回溯算法。
function solve()
for (let row = 0; row < 9; row++)
for (let column = 0; column < 9; column++)
if (gameState[row][column] == 0)
for (let value = 1; value <= 9; value++)
if (possible(row, column, value))
gameState[row][column] = value;
solve();
gameState[row][column] = 0;
return;
console.log(gameState);
return;
function possible(y, x, n)
let boardCopy = JSON.parse(JSON.stringify(gameState));
boardCopy[y][x] = n;
return isUnitUnique(boardCopy[y]) && isUnitUnique(getColumnUnit(boardCopy, x)) && isUnitUnique(getBlockUnit(boardCopy, x, y))
此控制台记录初始游戏状态不变。通过进行一些调试,我可以看到该算法正在通过网格,但是它似乎没有保留我对网格所做的更改。
提前致谢。
这里建议的是我的代码的可运行版本。当我在 sn-p 中运行它时它可以工作。使用 chrome 时不会。
var gameState = [
[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]
]
function solve()
for (let row = 0; row < 9; row++)
for (let column = 0; column < 9; column++)
if (gameState[row][column] == 0)
for (let value = 1; value <= 9; value++)
if (possible(row, column, value))
gameState[row][column] = value;
solve();
gameState[row][column] = 0;
return;
console.log(gameState);
return;
function possible(y, x, n)
let boardCopy = JSON.parse(JSON.stringify(gameState));
boardCopy[y][x] = n;
return isUnitUnique(boardCopy[y]) && isUnitUnique(getColumnUnit(boardCopy, x)) && isUnitUnique(getBlockUnit(boardCopy, x, y))
function isUnitUnique(unit)
for (let value = 1; value <= 9; value++)
let tally = 0;
for (let index = 0; index < 9; index++)
if (unit[index] == value)
tally++;
if (tally > 1)
return false;
return true;
function getColumnUnit(board, column)
let unit = [];
for (let row = 0; row < 9; row++)
unit.push(board[row][column]);
return unit;
function getBlockUnit(board, x, y)
let unit = []
if (x >= 0 && x <= 2) j = 1;
else if (x >= 3 && x <= 5) j = 4;
else if (x >= 6 && x <= 8) j = 7;
if (y >= 0 && y <= 2) i = 1;
else if (y >= 3 && y <= 5) i = 4;
else if (y >= 6 && y <= 8) i = 7;
unit.push(board[i - 1][j - 1]);
unit.push(board[i - 1][j]);
unit.push(board[i - 1][j + 1]);
unit.push(board[i][j - 1]);
unit.push(board[i][j]);
unit.push(board[i][j + 1]);
unit.push(board[i + 1][j - 1]);
unit.push(board[i + 1][j]);
unit.push(board[i + 1][j + 1]);
return unit;
solve()
【问题讨论】:
这条线gameState[row][column] = 0;
应该做什么?
我明白了,这是回溯的一部分。但是,当它到达答案时会发生什么,它不会在返回的过程中将所有内容归零吗?
@bcr666 当它直接在上面调用solve() 时,它进入函数,游戏状态更新为新的潜在变量。它会一直这样做,直到达到没有有效值 (1-9) 的点并且它到达第一个 return 语句。然后从先前重新进入函数并将值更改为零,因为之前的值无法找到解决方案。最终,它到达网格上不再有 0 值的情况,它退出 for 循环并输出网格。我将添加一个 youtube 视频,显示它是在 python 中完成的。
Go 链接、Python 的 YouTube 视频、***链接...缺少的是 minimal reproducible example,它显示了正在运行的代码,包括它不工作的部分。您可能可以使用Stack Snippets 创建在 Stack Overflow 上运行她的东西,这样人们就不需要去其他网站找出您的问题。谢谢。
@HereticMonkey 感谢您的建议。我添加了代码的堆栈 sn-p。有趣的是,它运行并产生了预期的输出。我想知道为什么它不在我的 VS Code / chrome 环境中本地显示。
【参考方案1】:
感谢@HereticMonkey 建议我使用stack sn-ps 来分享我的代码。这样做时,我意识到它在这种环境中工作,并且它与在导致问题的浏览器(Chrome)中运行代码有关。还要感谢@bcr666,他还提到该算法可能会在返回的路上自动归零。
问题中列出的代码都是正确的。我添加了一项检查以防止函数在达到退出条件后继续执行。
let solved = false
function solve()
for (let row = 0; row < 9; row++)
for (let column = 0; column < 9; column++)
if (gameState[row][column] == 0)
for (let value = 1; value <= 9; value++)
if (possible(row, column, value))
gameState[row][column] = value;
solve();
if (!solved)
gameState[row][column] = 0;
return;
solved = true;
console.log(gameState);
return;
在回溯修复此问题之前检查游戏是否已解决。在它记录结果之前,它试图找到更多的解决方案。
【讨论】:
以上是关于Javascript递归回溯数独求解器的主要内容,如果未能解决你的问题,请参考以下文章