用回溯算法求解數獨問題

做者:Christina

翻譯:瘋狂的技術宅javascript

原文:https://dev.to/christinamcmah...前端

未經容許嚴禁轉載java

前幾天咱們在《淺析常見的算法範式》中討論了一些常見的算法範式,可是還留下了回溯算法沒有解決。本文來研究回溯算法。程序員

回溯是經過逐步構建解決方案來解決遞歸問題的算法。一般回溯從可能的解決方案開始,若是它不起做用,則須要回溯並嘗試另外一種解決方案,直到找到可行的解決方案爲止。回溯在解決 CSP(約束知足問題)時特別有用,例如填字遊戲、迷宮和數獨等。面試

一般回溯算法可用於如下三種類型的問題:算法

  1. 須要找到可行解決方案的決策問題
  2. 須要找到最佳解決方案的優化問題
  3. 須要找到一組可行解決方案的列舉問題

在本文中,我將經過解決數獨問題來演示回溯策略。segmentfault

解決數獨問題

針對此類問題的回溯算法會嘗試在每一個空格中列舉全部的數字,直到問題被解決爲止。先從 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));

如下是經過回溯法求解數獨問題的模擬動畫:

sudoku being solved by backtracking

但願本文能幫你理解回溯算法,之後我門還會再討論更多有趣的算法。

173382ede7319973.gif


本文首發微信公衆號:前端先鋒

歡迎掃描二維碼關注公衆號,天天都給你推送新鮮的前端技術文章

歡迎掃描二維碼關注公衆號,天天都給你推送新鮮的前端技術文章

歡迎繼續閱讀本專欄其它高贊文章:


相關文章
相關標籤/搜索