我正在尝试编写一个可以解决数独的算法。现在,我的代码一直有效,直到supplyGrid超出数字。当它发生时它应该回去尝试另一个号码,对吧?说实话,我不知道如何实现这一点。
var grid = [
[0, 0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0, 0]
],
supplyGrid = [1, 2, 3, 4, 5, 6, 7, 8, 9],
row = 0,
col = 0,
value = 0,
index = 0;
var solveSudoku = function (grid, row, col) {
if (col > 8) {
row++;
col = 0;
if (row > 8 && col > 8) {
console.log(grid);
return;
}
}
if (grid[row][col] === 0) { //
index = Math.floor(Math.random() * supplyGrid.length);
value = supplyGrid[index];
if (isValid(row, col, value)) {
grid[row][col] = value;
col++;
supplyGrid = [1, 2, 3, 4, 5, 6, 7, 8, 9];
solveSudoku(grid, row, col);
} else {
supplyGrid.splice(index, 1);
console.log(supplyGrid);
if (supplyGrid.length < 1) {
//backtrack();
console.log('Out of numbers');
return;
}
solveSudoku(grid, row, col);
}
} else { //row = 3, col = 5
solveSudoku(grid, row, ++col);
}
return this;
}
function isValid(row, col, value) {
if ((validateColumn(row, col, value)) || (validateRow(row, col, value)) || (validateBox(row, col, value))) {
return false;
} else {
return true;
}
}
function validateBox(row, col, value) {
row = Math.floor(row / 3) * 3;
col = Math.floor(col / 3) * 3;
var isFound = false;
for (var i = 0; i < 3; i++) {
for (var j = 0; j < 3; j++) {
if (grid[row + i][col + j] == value) isFound = true;
}
}
return isFound;
}
function validateRow(row, col, value) {
var isFound = false;
for (var i = 0; i < 9; i++) {
if (grid[row][i] === value) isFound = true;
}
return isFound;
}
function validateColumn(row, col, value) {
var isFound = false;
for (var i = 0; i < 9; i++) {
if (grid[i][col] === value) isFound = true;
}
return isFound;
}
答案 0 :(得分:3)
这是一个提供javascript
解决方案的开源存储库:
https://github.com/pocketjoso/sudokuJS
以下是详细介绍其解决方案的代码:
https://github.com/pocketjoso/sudokuJS/blob/master/sudokuJS.js
答案 1 :(得分:3)
我想出了这个解决方案,请检查一下:
function sudoku(grid: number[][]): boolean {
const rows: Set<number>[] = [];
const cols: Set<number>[] = [];
const miniGrids: Set<number>[] = [];
for (let i = 0; i < 9; ++i) {
rows.push(new Set());
cols.push(new Set());
miniGrids.push(new Set());
}
for (let i = 0; i < 9; i++) {
for (let j = 0; j < 9; j++) {
const gridId: number = Math.trunc(i / 3) * 3 + Math.trunc(j / 3);
const n: number = grid[i][j];
if (rows[i].has(n) || cols[j].has(n) || miniGrids[gridId].has(n)) return false;
rows[i].add(n);
cols[j].add(n);
miniGrids[gridId].add(n);
}
}
return true;
}
答案 2 :(得分:1)
我通过回溯算法解决了这个问题:
const _board = [
['.', '9', '.', '.', '4', '2', '1', '3', '6'],
['.', '.', '.', '9', '6', '.', '4', '8', '5'],
['.', '.', '.', '5', '8', '1', '.', '.', '.'],
['.', '.', '4', '.', '.', '.', '.', '.', '.'],
['5', '1', '7', '2', '.', '.', '9', '.', '.'],
['6', '.', '2', '.', '.', '.', '3', '7', '.'],
['1', '.', '.', '8', '.', '4', '.', '2', '.'],
['7', '.', '6', '.', '.', '.', '8', '1', '.'],
['3', '.', '.', '.', '9', '.', '.', '.', '.'],
];
sodokoSolver(_board);
console.log(_board);
function isValid(board, row, col, k) {
for (let i = 0; i < 9; i++) {
const m = 3 * Math.floor(row / 3) + Math.floor(i / 3);
const n = 3 * Math.floor(col / 3) + i % 3;
if (board[row][i] == k || board[i][col] == k || board[m][n] == k) {
return false;
}
}
return true;
}
function sodokoSolver(data) {
for (let i = 0; i < 9; i++) {
for (let j = 0; j < 9; j++) {
if (data[i][j] == '.') {
for (let k = 1; k <= 9; k++) {
if (isValid(data, i, j, k)) {
data[i][j] = `${k}`;
if (sodokoSolver(data)) {
return true;
} else {
data[i][j] = '.';
}
}
}
return false;
}
}
}
return true;
}
答案 3 :(得分:1)
这是一种更简单的方法。基本上,您可以使用哈希对象来跟踪行,列和子框。
const _board = [
['.', '9', '.', '.', '4', '2', '1', '3', '6'],
['.', '.', '.', '9', '6', '.', '4', '8', '5'],
['.', '.', '.', '5', '8', '1', '.', '.', '.'],
['.', '.', '4', '.', '.', '.', '.', '.', '.'],
['5', '1', '7', '2', '.', '.', '9', '.', '.'],
['6', '.', '2', '.', '.', '.', '3', '7', '.'],
['1', '.', '.', '8', '.', '4', '.', '2', '.'],
['7', '.', '6', '.', '.', '.', '8', '1', '.'],
['3', '.', '.', '.', '9', '.', '.', '.', '.'],
];
function isValidSudoku(grid) {
let seenRow = {},
seenCol = {},
seenSubBox = {},
seen = {}
for (let row = 0; row < 9; row++) {
for (let col = 0; col < 9; col++) {
let value = grid[row][col];
if (!(value === '.')) {
let rowKey = `${row}-${value}`,
colKey = `${col}-${value}`,
boxKey = `${Math.floor(row/3)}-${value}-${Math.floor(col/3)}`
if (seenRow[rowKey] || seenCol[colKey] || seenSubBox[boxKey]) {
return false;
}
seenRow[rowKey] = true;
seenCol[colKey] = true;
seenSubBox[boxKey] = true;
}
}
}
return true;
}
console.log(isValidSudoku(_board));
答案 4 :(得分:1)
我写了一个解决任何平方数独的解决方案,因为答案只能解决9x9平方。
https://github.com/Eomm/grokking/blob/master/games/sudoku/play-sudoku.js
该算法始终是回溯算法,但已对其进行了优化,以减少忽略默认单元格并使用随机统计信息选择值(又称为random :)的迭代次数(又称时间)。
与始终迭代 768.028 次的第一个答案相比,此解决方案的运行范围从 3.556 (非常幸运)到 31.824 (并非如此)幸运),在100个测试中平均进行了 19.592 次迭代。当然,使用相同的输入进行比较。
代码在这里:
scr = curses.initscr()
new_window(scr)
答案 5 :(得分:1)
使用回溯和搜索。此外,您可以更好地可视化已解决的电路板。 看一下这个。
const b = null;
//test cases
const bd1 = [
[1, b, b, b, b, b, b, b, 7],
[b, b, b, b, b, b, b, b, b],
[b, b, b, 5, b, b, b, b, b],
[b, b, b, b, b, b, b, b, b],
[b, b, b, b, b, b, b, b, b],
[b, b, b, b, 1, b, b, b, b],
[b, b, b, b, b, b, b, b, b],
[b, 2, b, b, b, b, b, b, b],
[b, b, b, b, b, b, b, b, 6],
]
const bd2 = [
[1, b, 5, b, b, b, b, b, 3],
[3, b, b, b, b, b, b, b, b],
[b, b, b, b, 8, b, b, b, b],
[b, b, b, b, b, b, b, b, b],
[b, b, b, b, b, b, b, b, b],
[b, b, b, b, b, b, 4, b, b],
[b, b, b, b, b, b, b, b, b],
[b, 3, b, b, b, b, b, b, b],
[b, b, b, b, b, b, b, b, 9],
]
const bd3 = [
[b, b, b, b, b, b, b, b, b],
[b, b, b, b, b, b, b, b, b],
[b, b, b, b, b, b, b, b, b],
[b, b, b, b, b, b, b, b, b],
[b, b, b, b, b, b, b, b, b],
[b, b, b, b, b, b, b, b, b],
[b, b, b, b, b, b, b, b, b],
[b, b, b, b, b, b, b, b, b],
[b, b, b, b, b, b, b, b, b],
]
//words toughest 2012
const bdTf = [
[8, b, b, b, b, b, b, b, b],
[b, b, 3, 6, b, b, b, b, b],
[b, 7, b, b, 9, b, 2, b, b],
[b, 5, b, b, b, 7, b, b, b],
[b, b, b, b, 4, 5, 7, b, b],
[b, b, b, 1, b, b, b, 3, b],
[b, b, 1, b, b, b, b, 6, 8],
[b, b, 8, 5, b, b, b, 1, b],
[b, 9, b, b, b, b, 4, b, b],
]
function solveSudoku(board) {
if (solveSudokud(board)) {
return board
} else {
const possibilities = nextBoards(board)
const validBoards = keepOnlyValid(possibilities) //filterFunction
return searchForSolution(validBoards) //helperFunction :backtracking
}
}
function searchForSolution(boards) {
if (boards.length < 1) { //checking the board is empty
return false
} else {
var first = boards.shift() //tak̉es the first board off and stores in the variable
const tryPath = solveSudoku(first)
if (tryPath != false) { //checking if tryPath is false or not. if it is not false, we wil return the solved board
return tryPath
} else {
return searchForSolution(boards)
}
}
}
function solveSudokud(board) {
for (var i = 0; i < 9; i++) {
for (var j = 0; j < 9; j++) {
if (board[i][j] === null) {
return false
}
}
}
return true
}
//nextBoardFunction will generate 9 possiblities for a pissible sudoku board
function nextBoards(board) {
var res = [];
const firstEmpty = findEmptySquare(board)
if (firstEmpty != undefined) { //if firstEmpty = not defined , then we will start generating possiblities
const y = firstEmpty[0]
const x = firstEmpty[1]
for (var i = 1; i < 10; i++) {
var newBoard = [...board]
var row = [...newBoard[y]]
row[x] = i
newBoard[y] = row
res.push(newBoard)
}
}
return res // if firstEmpty does = undefined that means there are no possibilities left and return res.
}
function findEmptySquare(board) {
// board --> [int, int] | represents the x and y coordinates of the empty sq
for (var i = 0; i < 9; i++) {
for (var j = 0; j < 9; j++) {
if (board[i][j] == null) {
return [i, j]
}
}
}
}
//filter funtion
function keepOnlyValid(boards) {
return boards.filter(b => validBoard(b)) //filtered version of the board array will be returned
}
function validBoard(board) {
// check each row
for(let i=0; i<9; i++) {
if(!validate(board[i])) return false
}
//check each col
for(let i=0; i<9; i++) {
var arr = []
for(let j=0; j<9; j++) {
arr.push(board[j][i]);
}
if(!validate(arr)) return false;
}
//check each 3*3
let row = [[0,1,2], [3,4,5], [6,7,8]]
let col = [[0,1,2], [3,4,5], [6,7,8]]
for(let i of row) {
for(let j of col) {
let arr = [];
for(let num1 of i) {
for(let num2 of j){
arr.push(board[num1][num2]);
}
}
if(!validate(arr)) return false;
}
}
return true
}
function validate(arr) {
//check duplicates
let set1 = new Set();
for(let i=0; i< arr.length; i++) {
if(arr[i] === b) continue;
if(set1.has(arr[i])) return false
set1.add(arr[i]);
}
return true
}
//for better visualisation and able to check manually
function get_row(board, row) {
return board[row]
}
function print_cell(value) {
if (Array.isArray(value)) {
return '.'
} else if (value == null) {
return '.'
} else {
return value
}
}
function print_board(board) {
console.log()
for (i = 0; i < 9; i++) {
let row = get_row(board, i)
if (i % 3 == 0) {
console.log("|=======|=======|=======|")
}
console.log("|",
print_cell(row[0]), print_cell(row[1]), print_cell(row[2]), "|",
print_cell(row[3]), print_cell(row[4]), print_cell(row[5]), "|",
print_cell(row[6]), print_cell(row[7]), print_cell(row[8]), "|")
}
console.log("|=======|=======|=======|")
}
console.log('testcase 1')
print_board(bd1)
print_board(solveSudoku(bd1))
console.log('testcase 2')
print_board(bd2)
print_board(solveSudoku(bd2))
console.log('testcase 3')
print_board(bd3)
print_board(solveSudoku(bd3))
console.log('testcase 4')
print_board(bdTf)
print_board(solveSudoku(bdTf))
答案 6 :(得分:0)
这就是我现在所拥有的。
var grid = [
[0, 0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0, 0],
[8, 0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0, 0]
],
invalidGrid = [
[[], [], [], [], [], [], [], [], []],
[[], [], [], [], [], [], [], [], []],
[[], [], [], [], [], [], [], [], []],
[[], [], [], [], [], [], [], [], []],
[[], [], [], [], [], [], [], [], []],
[[], [], [], [], [], [], [], [], []],
[[], [], [], [], [], [], [], [], []],
[[], [], [], [], [], [], [], [], []],
[[], [], [], [], [], [], [], [], []]
],
supplyGrid = [1, 2, 3, 4, 5, 6, 7, 8, 9],
row = 0,
col = 0,
value = 0,
index = 0,
z = 0;
var solveSudoku = function (grid, row, col) {
if (row > 8 && col > 8) return;
if (col > 8) {
row++;
col = 0;
}
if (grid[row][col] === 0) {
index = Math.floor(Math.random() * supplyGrid.length);
value = supplyGrid[index];
if (isValid(row, col, value)) {
grid[row][col] = value;
var iArr = invalidGrid[row][col];
iArr.push(value)
col++;
supplyGrid = [1, 2, 3, 4, 5, 6, 7, 8, 9];
solveSudoku(grid, row, col);
} else {
supplyGrid.splice(index, 1);
if (supplyGrid.length < 1) {
col--;
if (col < 0) col = 8;
supplyGrid = [1, 2, 3, 4, 5, 6, 7, 8, 9];
for (var i = 0; i < invalidGrid[row][col].length; i++) {
supplyGrid.splice(supplyGrid.indexOf(invalidGrid[row][col][i]), 1)
}
grid[row][col] = 0;
solveSudoku(grid, row, col);
}
solveSudoku(grid, row, col);
}
} else { //row = 3, col = 5
col++;
solveSudoku(grid, row, col);
}
return this;
}
function displaySudoku() {
var string = '';
for (var i = 0; i < 9; i++) {
string += '<tr>';
for (var j = 0; j < 9; j++) {
string += '<td>';
string += `${grid[i][j]}`;
string += '</td>';
}
string += '</tr>';
}
document.write('<table>' + string + '</table>')
}
function isValid(row, col, value) {
if ((validateColumn(row, col, value)) || (validateRow(row, col, value)) || (validateBox(row, col, value))) {
return false;
} else {
return true;
}
}
function validateBox(row, col, value) {
row = Math.floor(row / 3) * 3;
col = Math.floor(col / 3) * 3;
var isFound = false;
for (var i = 0; i < 3; i++) {
for (var j = 0; j < 3; j++) {
if (grid[row + i][col + j] == value) isFound = true;
}
}
return isFound;
}
function validateRow(row, col, value) {
var isFound = false;
for (var i = 0; i < 9; i++) {
if (grid[row][i] === value) isFound = true;
}
return isFound;
}
function validateColumn(row, col, value) {
var isFound = false;
for (var i = 0; i < 9; i++) {
if (grid[i][col] === value) isFound = true;
}
return isFound;
}
有时我会生成有效的数独。有时..我认为这与删除以前的号码有关。但我不确定..
答案 7 :(得分:0)
我希望有人会回答..哦......我的代码工作得很好。有时我会得到'无法读取未定义'的属性'长度'。还在努力..
我有另一个问题。我有2个网格。一个是原始网格,用户放置数字(其余部分用0填充),第二个网格用于完成所有操作。
这是一个问题。它可以解决游戏,但它会改变用户填充的数字。 1到3个数字被更改。剩下的很好。我不知道为什么会这样。
function createCopy() { //create copy of original grid
var copyGrid = [9];
for (var i = 0; i < 9; i++) {
copyGrid[i] = []
for (j = 0; j < 9; j++) {
if (originalGrid[i][j] === 0) {
copyGrid[i][j] = undefined; //put undefined instead of 0
} else {
copyGrid[i][j] = originalGrid[i][j];
}
}
}
return copyGrid;
}
var solveSudoku = function (grid, row, col) { //solver
if (row > 7 && col > 8) return; //end function when it reaches the end
if (col > 8) {
row++;
col = 0;
}
if (grid[row][col] === undefined) { //if there's no number
index = Math.floor(Math.random() * supplyGrid.length);
value = supplyGrid[index];
if (isValid(row, col, value)) { //validate row, col and box
grid[row][col] = value; //assigns the value
if (backward) { //checking if the algorithm went back to change the previous value
z = 1;
backward = false;
};
var iArr = invalidGrid[row][col];
iArr.push(value)
col++; //if value is fine, increase column by 1
supplyGrid = [1, 2, 3, 4, 5, 6, 7, 8, 9]; //reset supply gird
solveSudoku(grid, row, col);
} else { //if the number is invalid (cannot be placed in a row, column or box)
supplyGrid.splice(index, 1); //delete the number from supply grid. I dont want to use the same value again in the same field.
//console.log(supplyGrid);
if (supplyGrid.length < 1) { //if there's no number left (all numbers are invalid for this field)
changePreviousValue(grid, row, col); //go back to the previous field
}
solveSudoku(grid, row, col);
}
} else { //if number exist in this field
col++; //increase the col value
solveSudoku(grid, row, col);
}
return this;
}
function changePreviousValue(grid, row, col) {
// var lastRowIndex = row;
// var lastColIndex = col;
col--; //go back one field
if (col < 0) {
col = 8;
row--;
}
if (originalGrid[row][col] === grid[row][col]) { //here im checking the value in this field. If it's in originalGrid that means I dont want to do anything with it. I dont want to change that number.
changePreviousValue(grid, row, col); // callin function again with decreased col value (so we went back by 2 field)
z++;
}
backward = true;
if (z > 1) { //whether the algorithm went back by at least 2 fields
if (col > 7) {
row++;
col = -1;
}
var iArr = invalidGrid[row][col + 1];
iArr.splice(0, invalidGrid[row][col + 1].length); //delete used values in the next field.
}
z++;
supplyGrid = [1, 2, 3, 4, 5, 6, 7, 8, 9]; //reset supply grid
//col = -1;
for (var i = 0; i < invalidGrid[row][col].length; i++) {
supplyGrid.splice(supplyGrid.indexOf(invalidGrid[row][col][i]), 1) //here im getting UNDEFINED sometimes. Its resposible for deleting used numbers in ACTUAL field. f.e I used 6 before, in the next field it couldn't fit any number so it went back to the same field. I dont want use 6 again cuz it wasnt working.
}
grid[row][col] = undefined; //set field value to undefined, so we can assign new number.
solveSudoku(grid, row, col);
}
非常凌乱,我知道:|