递归数独算法有效,但 returns 不正确
Recursive sudoku algorithm works but returns incorrectly
抱歉下面的代码转储...!
该算法确实解决了数独难题(如 log in solve if empty == -1 func)但是一旦所有递归展开后的实际输出要么返回原始拼图字符串,要么返回默认大小写 {错误:'Puzzle cannot be solved'}.
我认为这可能是最终迭代错误,但我已经重构和重组,直到我脸色发青,无法弄清楚为什么它在展开时没有返回成功的输出。
如有任何帮助,我们将不胜感激。
class SudokuSolver {
validate(puzzleString) {
//console.log(puzzleString)
if (puzzleString.length === 0) {
return {
error: 'Required field missing'
}
}
if (puzzleString.length !== 81) {
return {
error: 'Expected puzzle to be 81 characters long'
}
}
if (!/^[0-9.]*$/.test(puzzleString)) {
return {
error: 'Invalid characters in puzzle'
};
}
return true
}
createBoard(puzzleString) {
//create a new row on 0th or 9th iteration
let puzzleBoard = [
[],
[],
[],
[],
[],
[],
[],
[],
[]
]
let puzzle = puzzleString.split('');
let row = -1
for (let i = 0; i < puzzle.length; i++) {
if (i % 9 === 0) {
row++
}
puzzleBoard[row].push(puzzle[i])
}
return puzzleBoard
}
checkRowPlacement(board, row, column, value) {
for (let i = 0; i < 9; i++) {
if (board[row][i] == value) {
return {
valid: false,
conflict: 'row'
}
}
}
return {
valid: true
}
}
checkColPlacement(board, row, column, value) {
for (let i = 0; i < 9; i++) {
if (board[i][column] == value) {
return {
valid: false,
conflict: 'column'
};
}
}
return {
valid: true
};
}
checkRegionPlacement(board, row, column, value) {
let regionStartRow = parseInt(row / 3) * 3;
let regionStartCol = parseInt(column / 3) * 3;
for (let i = regionStartRow; i < regionStartRow + 3; i++) {
for (let j = regionStartCol; j < regionStartCol + 3; j++) {
if (board[i][j] == value) {
return {
valid: false,
conflict: 'region'
}
}
}
}
return {
valid: true
}
}
coordToBoard(input) {
let res = [];
// A1 -> A = 0, 1 = 0
res.push(input.toUpperCase().charCodeAt(0) - 65)
res.push(parseInt(input.charAt(1)) - 1)
return res
}
checkPlacement(puzzleString, coordinate, value) {
if (!puzzleString || !coordinate || !value) {
return {
error: 'Required field(s) missing'
}
}
if (this.validate(puzzleString) != true) {
return this.validate(puzzleString)
};
let coordSplit = coordinate.split('')
if (!/[A-Ia-i]/.test(coordSplit[0] || !/[0-9]/.test(coordSplit[1]))) {
return {
error: 'Invalid coordinate'
}
};
if (value < 1 || value > 9 || isNaN(value)) {
return {
error: 'Invalid value'
}
}
let boardCoords = this.coordToBoard(coordinate)
//console.log("boardCoords: " + boardCoords)
let board = this.createBoard(puzzleString)
//console.log(board)
let conflicts = []
if (this.checkRowPlacement(board, boardCoords[0], boardCoords[1], value).valid != true) {
conflicts.push(this.checkRowPlacement(board, boardCoords[0], boardCoords[1], value).conflict)
}
if (this.checkColPlacement(board, boardCoords[0], boardCoords[1], value).valid != true) {
conflicts.push(this.checkColPlacement(board, boardCoords[0], boardCoords[1], value).conflict)
}
if (this.checkRegionPlacement(board, boardCoords[0], boardCoords[1], value).valid != true) {
conflicts.push(this.checkRegionPlacement(board, boardCoords[0], boardCoords[1], value).conflict)
}
//if value is the same as current coordinate make blank and check if valid and if so return true
if (board[boardCoords[0]][boardCoords[1]] == value) {
board[boardCoords[0]][boardCoords[1]] = '.'
if (this.checkRegionPlacement(board, boardCoords[0], boardCoords[1], value).valid == true && this.checkColPlacement(board, boardCoords[0], boardCoords[1], value).valid == true && this.checkRowPlacement(board, boardCoords[0], boardCoords[1], value).valid == true) {
return {
valid: true
}
}
}
if (this.checkRegionPlacement(board, boardCoords[0], boardCoords[1], value).valid != true || this.checkColPlacement(board, boardCoords[0], boardCoords[1], value).valid != true || this.checkRowPlacement(board, boardCoords[0], boardCoords[1], value).valid != true) {
return {
valid: false,
conflict: conflicts
}
}
return {
valid: true
}
}
findUnassignedLocation(puzzleString) {
return puzzleString.indexOf('.')
}
rowColToCoord(index) {
let col = (index % 9) + 1;
let row = String.fromCharCode(parseInt(index / 9) + 65)
return row + col
}
solve(puzzleString) {
if (!puzzleString) {
return {
error: 'Required field missing'
}
}
if (this.validate(puzzleString) != true) {
return this.validate(puzzleString)
}
let empty = this.findUnassignedLocation(puzzleString)
if (empty == -1) {
console.log('no empties: ' + puzzleString)
return {solution: puzzleString}
} else {
let coord = this.rowColToCoord(empty)
for (var num = 1; num <= 9; num++) {
if (this.checkPlacement(puzzleString, coord, num).valid ) {
if (!this.solve((puzzleString.substr(0, empty) + num + puzzleString.substr(empty + 1))).error) {
console.log('no error: ' + puzzleString)
return {solution: puzzleString}
}
}
}
//console.log('Cannot be solved: ' + puzzleString)
return {error: 'Puzzle cannot be solved'}
}
}
}
module.exports = SudokuSolver;
这是日志的输出:
Cannot be solved: 7692354188514763.2432......1...69.83.9.....6.62.71...9......1945....4.37.4.3..6..
Cannot be solved: 7692354188514769.2432......1...69.83.9.....6.62.71...9......1945....4.37.4.3..6..
Cannot be solved: 769235418851476..2432......1...69.83.9.....6.62.71...9......1945....4.37.4.3..6..
Cannot be solved: 76923541885147...2432......1...69.83.9.....6.62.71...9......1945....4.37.4.3..6..
Cannot be solved: 7692354188514963724321785..1...69.83.9.....6.62.71...9......1945....4.37.4.3..6..
Cannot be solved: 76923541885149637243217895617456928339582..6.62.71...9......1945....4.37.4.3..6..
no empties: 769235418851496372432178956174569283395842761628713549283657194516924837947381625
no error: 76923541885149637243217895617456928339584276162871354928365719451692483794738162.
no error: 7692354188514963724321789561745692833958427616287135492836571945169248379473816..
no error: 76923541885149637243217895617456928339584276162871354928365719451692483794738.6..
no error: 7692354188514963724321789561745692833958427616287135492836571945169248379473..6..
no error: 76923541885149637243217895617456928339584276162871354928365719451692483794.3..6..
no error: 769235418851496372432178956174569283395842761628713549283657194516924837.4.3..6..
no error: 769235418851496372432178956174569283395842761628713549283657194516924.37.4.3..6..
no error: 7692354188514963724321789561745692833958427616287135492836571945169.4.37.4.3..6..
no error: 769235418851496372432178956174569283395842761628713549283657194516..4.37.4.3..6..
no error: 76923541885149637243217895617456928339584276162871354928365719451...4.37.4.3..6..
no error: 7692354188514963724321789561745692833958427616287135492836571945....4.37.4.3..6..
no error: 76923541885149637243217895617456928339584276162871354928365.1945....4.37.4.3..6..
no error: 7692354188514963724321789561745692833958427616287135492836..1945....4.37.4.3..6..
no error: 769235418851496372432178956174569283395842761628713549283...1945....4.37.4.3..6..
no error: 76923541885149637243217895617456928339584276162871354928....1945....4.37.4.3..6..
no error: 7692354188514963724321789561745692833958427616287135492.....1945....4.37.4.3..6..
no error: 769235418851496372432178956174569283395842761628713549......1945....4.37.4.3..6..
no error: 7692354188514963724321789561745692833958427616287135.9......1945....4.37.4.3..6..
no error: 769235418851496372432178956174569283395842761628713..9......1945....4.37.4.3..6..
no error: 76923541885149637243217895617456928339584276162871...9......1945....4.37.4.3..6..
no error: 76923541885149637243217895617456928339584276162.71...9......1945....4.37.4.3..6..
no error: 76923541885149637243217895617456928339584276.62.71...9......1945....4.37.4.3..6..
no error: 769235418851496372432178956174569283395842.6.62.71...9......1945....4.37.4.3..6..
no error: 76923541885149637243217895617456928339584..6.62.71...9......1945....4.37.4.3..6..
no error: 7692354188514963724321789561745692833958...6.62.71...9......1945....4.37.4.3..6..
no error: 769235418851496372432178956174569283395....6.62.71...9......1945....4.37.4.3..6..
no error: 76923541885149637243217895617456928339.....6.62.71...9......1945....4.37.4.3..6..
no error: 769235418851496372432178956174569283.9.....6.62.71...9......1945....4.37.4.3..6..
no error: 769235418851496372432178956174569.83.9.....6.62.71...9......1945....4.37.4.3..6..
no error: 769235418851496372432178956174.69.83.9.....6.62.71...9......1945....4.37.4.3..6..
no error: 76923541885149637243217895617..69.83.9.....6.62.71...9......1945....4.37.4.3..6..
no error: 7692354188514963724321789561...69.83.9.....6.62.71...9......1945....4.37.4.3..6..
no error: 76923541885149637243217895.1...69.83.9.....6.62.71...9......1945....4.37.4.3..6..
no error: 7692354188514963724321789..1...69.83.9.....6.62.71...9......1945....4.37.4.3..6..
no error: 769235418851496372432178...1...69.83.9.....6.62.71...9......1945....4.37.4.3..6..
no error: 76923541885149637243217....1...69.83.9.....6.62.71...9......1945....4.37.4.3..6..
no error: 7692354188514963724321.....1...69.83.9.....6.62.71...9......1945....4.37.4.3..6..
no error: 769235418851496372432......1...69.83.9.....6.62.71...9......1945....4.37.4.3..6..
no error: 7692354188514963.2432......1...69.83.9.....6.62.71...9......1945....4.37.4.3..6..
no error: 769235418851496..2432......1...69.83.9.....6.62.71...9......1945....4.37.4.3..6..
no error: 76923541885149...2432......1...69.83.9.....6.62.71...9......1945....4.37.4.3..6..
no error: 7692354188514....2432......1...69.83.9.....6.62.71...9......1945....4.37.4.3..6..
no error: 76923541885.4....2432......1...69.83.9.....6.62.71...9......1945....4.37.4.3..6..
no error: 76923541.85.4....2432......1...69.83.9.....6.62.71...9......1945....4.37.4.3..6..
no error: 769235.1.85.4....2432......1...69.83.9.....6.62.71...9......1945....4.37.4.3..6..
no error: 7692.5.1.85.4....2432......1...69.83.9.....6.62.71...9......1945....4.37.4.3..6..
no error: 769..5.1.85.4....2432......1...69.83.9.....6.62.71...9......1945....4.37.4.3..6..
no error: 7.9..5.1.85.4....2432......1...69.83.9.....6.62.71...9......1945....4.37.4.3..6..
no error: ..9..5.1.85.4....2432......1...69.83.9.....6.62.71...9......1945....4.37.4.3..6..
原因是您的代码忽略了递归调用可能返回的解决方案,而只是 returns 最初调用时使用的不完整拼图:
if (!this.solve((puzzleString.substr(0, empty) + num + puzzleString.substr(empty + 1))).error) {
console.log('no error: ' + puzzleString)
return {solution: puzzleString} // Wrong. This is not the solution, but the unfinished puzzle.
}
改为这样做:
let result = this.solve((puzzleString.substr(0, empty) + num + puzzleString.substr(empty + 1)));
if (!result.error) {
console.log('no error: ' + puzzleString)
return result; // Return what you got from the recursive call (the solution)
}
抱歉下面的代码转储...!
该算法确实解决了数独难题(如 log in solve if empty == -1 func)但是一旦所有递归展开后的实际输出要么返回原始拼图字符串,要么返回默认大小写 {错误:'Puzzle cannot be solved'}.
我认为这可能是最终迭代错误,但我已经重构和重组,直到我脸色发青,无法弄清楚为什么它在展开时没有返回成功的输出。
如有任何帮助,我们将不胜感激。
class SudokuSolver {
validate(puzzleString) {
//console.log(puzzleString)
if (puzzleString.length === 0) {
return {
error: 'Required field missing'
}
}
if (puzzleString.length !== 81) {
return {
error: 'Expected puzzle to be 81 characters long'
}
}
if (!/^[0-9.]*$/.test(puzzleString)) {
return {
error: 'Invalid characters in puzzle'
};
}
return true
}
createBoard(puzzleString) {
//create a new row on 0th or 9th iteration
let puzzleBoard = [
[],
[],
[],
[],
[],
[],
[],
[],
[]
]
let puzzle = puzzleString.split('');
let row = -1
for (let i = 0; i < puzzle.length; i++) {
if (i % 9 === 0) {
row++
}
puzzleBoard[row].push(puzzle[i])
}
return puzzleBoard
}
checkRowPlacement(board, row, column, value) {
for (let i = 0; i < 9; i++) {
if (board[row][i] == value) {
return {
valid: false,
conflict: 'row'
}
}
}
return {
valid: true
}
}
checkColPlacement(board, row, column, value) {
for (let i = 0; i < 9; i++) {
if (board[i][column] == value) {
return {
valid: false,
conflict: 'column'
};
}
}
return {
valid: true
};
}
checkRegionPlacement(board, row, column, value) {
let regionStartRow = parseInt(row / 3) * 3;
let regionStartCol = parseInt(column / 3) * 3;
for (let i = regionStartRow; i < regionStartRow + 3; i++) {
for (let j = regionStartCol; j < regionStartCol + 3; j++) {
if (board[i][j] == value) {
return {
valid: false,
conflict: 'region'
}
}
}
}
return {
valid: true
}
}
coordToBoard(input) {
let res = [];
// A1 -> A = 0, 1 = 0
res.push(input.toUpperCase().charCodeAt(0) - 65)
res.push(parseInt(input.charAt(1)) - 1)
return res
}
checkPlacement(puzzleString, coordinate, value) {
if (!puzzleString || !coordinate || !value) {
return {
error: 'Required field(s) missing'
}
}
if (this.validate(puzzleString) != true) {
return this.validate(puzzleString)
};
let coordSplit = coordinate.split('')
if (!/[A-Ia-i]/.test(coordSplit[0] || !/[0-9]/.test(coordSplit[1]))) {
return {
error: 'Invalid coordinate'
}
};
if (value < 1 || value > 9 || isNaN(value)) {
return {
error: 'Invalid value'
}
}
let boardCoords = this.coordToBoard(coordinate)
//console.log("boardCoords: " + boardCoords)
let board = this.createBoard(puzzleString)
//console.log(board)
let conflicts = []
if (this.checkRowPlacement(board, boardCoords[0], boardCoords[1], value).valid != true) {
conflicts.push(this.checkRowPlacement(board, boardCoords[0], boardCoords[1], value).conflict)
}
if (this.checkColPlacement(board, boardCoords[0], boardCoords[1], value).valid != true) {
conflicts.push(this.checkColPlacement(board, boardCoords[0], boardCoords[1], value).conflict)
}
if (this.checkRegionPlacement(board, boardCoords[0], boardCoords[1], value).valid != true) {
conflicts.push(this.checkRegionPlacement(board, boardCoords[0], boardCoords[1], value).conflict)
}
//if value is the same as current coordinate make blank and check if valid and if so return true
if (board[boardCoords[0]][boardCoords[1]] == value) {
board[boardCoords[0]][boardCoords[1]] = '.'
if (this.checkRegionPlacement(board, boardCoords[0], boardCoords[1], value).valid == true && this.checkColPlacement(board, boardCoords[0], boardCoords[1], value).valid == true && this.checkRowPlacement(board, boardCoords[0], boardCoords[1], value).valid == true) {
return {
valid: true
}
}
}
if (this.checkRegionPlacement(board, boardCoords[0], boardCoords[1], value).valid != true || this.checkColPlacement(board, boardCoords[0], boardCoords[1], value).valid != true || this.checkRowPlacement(board, boardCoords[0], boardCoords[1], value).valid != true) {
return {
valid: false,
conflict: conflicts
}
}
return {
valid: true
}
}
findUnassignedLocation(puzzleString) {
return puzzleString.indexOf('.')
}
rowColToCoord(index) {
let col = (index % 9) + 1;
let row = String.fromCharCode(parseInt(index / 9) + 65)
return row + col
}
solve(puzzleString) {
if (!puzzleString) {
return {
error: 'Required field missing'
}
}
if (this.validate(puzzleString) != true) {
return this.validate(puzzleString)
}
let empty = this.findUnassignedLocation(puzzleString)
if (empty == -1) {
console.log('no empties: ' + puzzleString)
return {solution: puzzleString}
} else {
let coord = this.rowColToCoord(empty)
for (var num = 1; num <= 9; num++) {
if (this.checkPlacement(puzzleString, coord, num).valid ) {
if (!this.solve((puzzleString.substr(0, empty) + num + puzzleString.substr(empty + 1))).error) {
console.log('no error: ' + puzzleString)
return {solution: puzzleString}
}
}
}
//console.log('Cannot be solved: ' + puzzleString)
return {error: 'Puzzle cannot be solved'}
}
}
}
module.exports = SudokuSolver;
这是日志的输出:
Cannot be solved: 7692354188514763.2432......1...69.83.9.....6.62.71...9......1945....4.37.4.3..6..
Cannot be solved: 7692354188514769.2432......1...69.83.9.....6.62.71...9......1945....4.37.4.3..6..
Cannot be solved: 769235418851476..2432......1...69.83.9.....6.62.71...9......1945....4.37.4.3..6..
Cannot be solved: 76923541885147...2432......1...69.83.9.....6.62.71...9......1945....4.37.4.3..6..
Cannot be solved: 7692354188514963724321785..1...69.83.9.....6.62.71...9......1945....4.37.4.3..6..
Cannot be solved: 76923541885149637243217895617456928339582..6.62.71...9......1945....4.37.4.3..6..
no empties: 769235418851496372432178956174569283395842761628713549283657194516924837947381625
no error: 76923541885149637243217895617456928339584276162871354928365719451692483794738162.
no error: 7692354188514963724321789561745692833958427616287135492836571945169248379473816..
no error: 76923541885149637243217895617456928339584276162871354928365719451692483794738.6..
no error: 7692354188514963724321789561745692833958427616287135492836571945169248379473..6..
no error: 76923541885149637243217895617456928339584276162871354928365719451692483794.3..6..
no error: 769235418851496372432178956174569283395842761628713549283657194516924837.4.3..6..
no error: 769235418851496372432178956174569283395842761628713549283657194516924.37.4.3..6..
no error: 7692354188514963724321789561745692833958427616287135492836571945169.4.37.4.3..6..
no error: 769235418851496372432178956174569283395842761628713549283657194516..4.37.4.3..6..
no error: 76923541885149637243217895617456928339584276162871354928365719451...4.37.4.3..6..
no error: 7692354188514963724321789561745692833958427616287135492836571945....4.37.4.3..6..
no error: 76923541885149637243217895617456928339584276162871354928365.1945....4.37.4.3..6..
no error: 7692354188514963724321789561745692833958427616287135492836..1945....4.37.4.3..6..
no error: 769235418851496372432178956174569283395842761628713549283...1945....4.37.4.3..6..
no error: 76923541885149637243217895617456928339584276162871354928....1945....4.37.4.3..6..
no error: 7692354188514963724321789561745692833958427616287135492.....1945....4.37.4.3..6..
no error: 769235418851496372432178956174569283395842761628713549......1945....4.37.4.3..6..
no error: 7692354188514963724321789561745692833958427616287135.9......1945....4.37.4.3..6..
no error: 769235418851496372432178956174569283395842761628713..9......1945....4.37.4.3..6..
no error: 76923541885149637243217895617456928339584276162871...9......1945....4.37.4.3..6..
no error: 76923541885149637243217895617456928339584276162.71...9......1945....4.37.4.3..6..
no error: 76923541885149637243217895617456928339584276.62.71...9......1945....4.37.4.3..6..
no error: 769235418851496372432178956174569283395842.6.62.71...9......1945....4.37.4.3..6..
no error: 76923541885149637243217895617456928339584..6.62.71...9......1945....4.37.4.3..6..
no error: 7692354188514963724321789561745692833958...6.62.71...9......1945....4.37.4.3..6..
no error: 769235418851496372432178956174569283395....6.62.71...9......1945....4.37.4.3..6..
no error: 76923541885149637243217895617456928339.....6.62.71...9......1945....4.37.4.3..6..
no error: 769235418851496372432178956174569283.9.....6.62.71...9......1945....4.37.4.3..6..
no error: 769235418851496372432178956174569.83.9.....6.62.71...9......1945....4.37.4.3..6..
no error: 769235418851496372432178956174.69.83.9.....6.62.71...9......1945....4.37.4.3..6..
no error: 76923541885149637243217895617..69.83.9.....6.62.71...9......1945....4.37.4.3..6..
no error: 7692354188514963724321789561...69.83.9.....6.62.71...9......1945....4.37.4.3..6..
no error: 76923541885149637243217895.1...69.83.9.....6.62.71...9......1945....4.37.4.3..6..
no error: 7692354188514963724321789..1...69.83.9.....6.62.71...9......1945....4.37.4.3..6..
no error: 769235418851496372432178...1...69.83.9.....6.62.71...9......1945....4.37.4.3..6..
no error: 76923541885149637243217....1...69.83.9.....6.62.71...9......1945....4.37.4.3..6..
no error: 7692354188514963724321.....1...69.83.9.....6.62.71...9......1945....4.37.4.3..6..
no error: 769235418851496372432......1...69.83.9.....6.62.71...9......1945....4.37.4.3..6..
no error: 7692354188514963.2432......1...69.83.9.....6.62.71...9......1945....4.37.4.3..6..
no error: 769235418851496..2432......1...69.83.9.....6.62.71...9......1945....4.37.4.3..6..
no error: 76923541885149...2432......1...69.83.9.....6.62.71...9......1945....4.37.4.3..6..
no error: 7692354188514....2432......1...69.83.9.....6.62.71...9......1945....4.37.4.3..6..
no error: 76923541885.4....2432......1...69.83.9.....6.62.71...9......1945....4.37.4.3..6..
no error: 76923541.85.4....2432......1...69.83.9.....6.62.71...9......1945....4.37.4.3..6..
no error: 769235.1.85.4....2432......1...69.83.9.....6.62.71...9......1945....4.37.4.3..6..
no error: 7692.5.1.85.4....2432......1...69.83.9.....6.62.71...9......1945....4.37.4.3..6..
no error: 769..5.1.85.4....2432......1...69.83.9.....6.62.71...9......1945....4.37.4.3..6..
no error: 7.9..5.1.85.4....2432......1...69.83.9.....6.62.71...9......1945....4.37.4.3..6..
no error: ..9..5.1.85.4....2432......1...69.83.9.....6.62.71...9......1945....4.37.4.3..6..
原因是您的代码忽略了递归调用可能返回的解决方案,而只是 returns 最初调用时使用的不完整拼图:
if (!this.solve((puzzleString.substr(0, empty) + num + puzzleString.substr(empty + 1))).error) {
console.log('no error: ' + puzzleString)
return {solution: puzzleString} // Wrong. This is not the solution, but the unfinished puzzle.
}
改为这样做:
let result = this.solve((puzzleString.substr(0, empty) + num + puzzleString.substr(empty + 1)));
if (!result.error) {
console.log('no error: ' + puzzleString)
return result; // Return what you got from the recursive call (the solution)
}