数独求解器在javascript中回溯

时间:2016-02-17 13:16:01

标签: javascript algorithm recursion sudoku backtracking

我正在尝试使用回溯在JavaScript中编写一个Sudoku求解器,我做了一些研究,看到了类似的问题,但我的方法不同;它不起作用。

这是JavaScript代码:

var grid = [ // Empty grid to test the solver
            [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]
           ];
var invalid = [ // Here go additional invalid numbers
               [[],[],[],[],[],[],[],[],[]],
               [[],[],[],[],[],[],[],[],[]],
               [[],[],[],[],[],[],[],[],[]],
               [[],[],[],[],[],[],[],[],[]],
               [[],[],[],[],[],[],[],[],[]],
               [[],[],[],[],[],[],[],[],[]],
               [[],[],[],[],[],[],[],[],[]],
               [[],[],[],[],[],[],[],[],[]],
               [[],[],[],[],[],[],[],[],[]],
              ];

function sudokuSolver() { // The solver function
    for (var r = 0; r < 9; r++) { // For each row
        for (var c = 0; c < 9; c++) { // For each column
            for (var n = 1; n < 11; n++) { // For each number + 10
                if (n == 10) { // If no solutions were found for the current cell, backtrack.
                    if (c == 0) { // If at left-most position
                        var x = grid[--r][8]; // Assign the number in the previous cell to "x". Decremented "r" because the previous cell is in the previous row, and the number in it should be changed
                        invalid[r+1][0] = []; // Reset invalid numbers for current cell
                        invalid[r][8].push(x); // Mark "x" as invalid in the previous cell
                        c = 7; // The number in the previous cell should be changed, the previous cell is in column 8, the loop increments each time the code block is executed, so c here should hold 8 - 1 = 7
                    } else { // Same goal here
                        var x = grid[r][--c]; 
                        invalid[r][c+1] = [];
                        invalid[r][c].push(x);
                        c--;
                    }
                    break;
                } else if (isValid(r, c, n)) {
                    grid[r][c] = n; // If n is valid, put it
                    console.log(r + ',' + c + ',' + n);
                    break; // And break the loop (it won't reach n = 10)
                }
            }
        }
    }

    // Print the solution
    for (var r = 0; r < 9; r++) {
        for (var c = 0; c < 9; c++) {
            document.getElementById('sudoku').getElementsByTagName('tr')[r].getElementsByTagName('td')[c].innerHTML = grid[r][c];
        }
    }
}

function isValid(r,c,n) {
    var a;
    var b = true;
    var col = '';
    for (var i = 0; i < 9; i++) {
        col += grid[i][c];
    }
    var sec = '';
    for (var lr = 3 * Math.floor(r / 3); lr < 3 * Math.floor(r / 3) + 3; lr++) {
        for (var lc = 3 * Math.floor(c / 3); lc < 3 * Math.floor(c / 3) + 3; lc++) {
            sec += grid[lr][lc];
        }
    }
    a = (grid[r].toString().indexOf(n) == -1 && col.indexOf(n) == -1 && sec.indexOf(n) == -1);
    for (var i = 0; i < invalid[r][c].length; i++) {
        if (n == invalid[r][c][i]) b = false; break;
    }
    return (a && b);
}

没有if (n == 10) { // Code }它做了预期的事。

有了它,浏览器停止响应,然后询问是否停止脚本。

在控制台中,我得到: 1,5,7 1,6,3 1,5,7 1,6,3 ...

任何有关错误的线索?

这是个主意:

(I)在当前单元格中尝试“1”,如果它有效,则将其放入网格数组并继续下一个单元格,否则,尝试“2”并再次检查,然后“3”如果“2”是无效等...如果找到有效数字,将其放入网格并继续进入下一个单元格,如果没有有效数字(达到10),清除当前单元格的无效数字(如果有),请在前一个单元格在那里无效(不会导致解决方案),然后去那里(回溯)。

(II)重复(I)直到Sudoku被解决。

问题已解决。

if (n == invalid[r][c][i]) b = false; break;

原意是:

if (n == invalid[r][c][i]) {b = false; break;}

1 个答案:

答案 0 :(得分:0)

你有一个无限循环

for (var c = 0; c < 9; c++) {…}

退出循环的唯一方法是c >= 9。通常,当c++最终递增时,会发生这种情况。但是,当n == 10时,您的逻辑将始终执行某种减量(--cc--c = 7),以防止c到达{{} 1}}。

因此,当引入9代码分支时,n == 10的递减在管理c的for循环的每次迭代中发生一次,因为递减是cc = 7它不会到达c = c - 2并结束循环。

答案是不要改变循环内的值。重构代码以管理9rc,就好像它们在for循环块中是不可变的一样。然后对块中的这些值执行计算,以查找执行业务逻辑所需的值。也许制作中间变量来存储中间值。

另一个重要的帮助是将逻辑的每个部分分解为函数。这样做可以帮助您抽象复杂性,以帮助您包围,因为它们是功能,它们可以是确定性的。意味着你输入的内容将产生可预测的输出。然后,只需要按顺序编写函数并想要执行业务逻辑。