Решатель судоку JavaScript застрял в бесконечном цикле для некоторых плат / не работает для всех плат - PullRequest
0 голосов
/ 07 марта 2019

Я новичок в stackoverflow. Я искал похожие вопросы, но никто не отвечает на мои вопросы. Я нахожусь в моем первом семестре информатики и в настоящее время беру курс программирования 1. Мы работаем только с JavaScript для всего курса, и поэтому JavaScript - единственный язык программирования, который я знаю до сих пор. Следовательно, у меня достаточно ограниченное понимание, интуитивное или иное, программирования в целом. Английский тоже не мой родной язык, так что прости меня за ошибки.

Тем не менее, вот моя проблема: мне нужно запрограммировать решатель судоку. Я действительно преуспел в кодировании решателя для судоку, который считается "легким". Это занимает долю секунды, поэтому я довольно доволен результатами. Проблема в том, что есть некоторые судоку, которые не работают, а именно те, которые считаются «трудными». Излишне говорить, что решатель должен работать на всех судоку. Ниже приведены фрагменты кода для примеров как «простых», так и «сложных» плат судоку, а также код для моего решателя. Я пытался как можно лучше аннотировать, чтобы описать функционирование, но есть определенная проблема, поскольку она не решит сложную. На самом деле он застрял в бесконечном цикле.

var easyBoard = [
    [1,0,0,3,0,0,9,5,2],
    [0,4,0,6,0,0,1,0,0],
    [3,0,0,1,0,0,0,0,0],
    [0,6,4,7,2,0,0,1,0],
    [8,7,0,9,0,6,0,2,4],
    [0,2,0,0,8,5,7,6,0],
    [0,0,0,0,0,1,0,0,7],
    [0,0,7,0,0,9,0,4,0],
    [2,3,9,0,0,4,0,0,1]  
];

var hardBoard = [
    [4,0,0,6,0,7,0,8,5],
    [0,0,0,0,0,0,6,0,0],
    [0,0,7,0,0,0,0,0,0],
    [0,5,0,0,0,3,0,0,4],
    [3,7,0,0,0,8,0,0,0],
    [6,0,0,2,0,0,0,0,0],
    [8,0,0,0,0,0,3,1,0],
    [0,3,1,0,4,9,0,0,0],
    [0,0,0,0,0,0,0,0,9]
];


var solve = function (board) {
    var empty = []; // We create an array for the 1x1 squares with no value, so we can call upon them with ease later on.
    for (var i = 0; i < 9; i++) {
        for (var j = 0; j < 9; j++) {
            if (board[i][j] === 0) {
                empty.push([i,j]);
            }
        }
    }
    for (var i = 0; i < empty.length;) { // We check every possible value for all empty 1x1 squares.
        var row = empty[i][0]; // Used for row and 3x3 square checks
        var column = empty[i][1]; // Used for column and 3x3 square checks
        var value = board[row][column] + 1; // We start at 1, because obviously 0 is not a Sudoku value.
        var found = false; // We assume the value is invalid, unless it passes all three tests.
        while (!found && value <= 9) { // As long as the value is invalid, we increase by one until it reaches more than 9.
            var equal = false; // We assume for now that the value is not equal to any other in its row, column or 3x3 square.
            for (var y = 0; y < 9; y++) {
                if (board[row][y] === value) {
                    equal = true;
                }
            }
            for (var x = 0; x < 9; x++) {
                if (board[x][column] === value) {
                    equal = true;
                }
            }
            for (var x = 3*Math.floor(row/3); x < 3*Math.floor(row/3)+3; x++) {
                for (var y = 3*Math.floor(column/3); y < 3*Math.floor(column/3)+3; y++) {
                    if (board[x][y] === value) {
                        equal = true;
                    }
                }
            }
            if (!equal) { // If the value is not equal to any other in its row, column or 3x3 square, it is valid.
                found = true; // We have found a valid value, for now.
                board[row][column] = value; // We assign said value to the corresponding board 1x1 square, for now.
                i++; // We then move on to the next empty 1x1 square.
            }
            else {
                value++; // If the value is invalid, we simply try the next possible value.
            }
        }
        if (!found) { // If, from 1 to 9, the value is invalid, it means the one before is invalid.
            board[row][column] = 0; // We then re-assign an empty value to the 1x1 square, before backtracking.
            i--; // We go back to the previous 1x1 square to try a different value.
        }
    }
};

//   test routines

var clone2 = array => array.slice().map( row=>row.slice());

function easyTest() {
    var board = clone2( easyBoard);
    solve( board);
    console.log( "easy board solution:");
    console.log( board);
}

function hardTest() {
    var board = clone2( hardBoard);
    solve( board);
    console.log( "hard board solution:");
    console.log( board);
}
<button type="button" onclick="easyTest()">Easy Test</button>
<button type="button" onclick="hardTest()">Hard Test</button>

Код работает для первого, но не для второго. Это потому, что алгоритм «обратного следования» и «грубой силы» недостаточно быстр для «жесткого» судоку? Это займет всего несколько часов или в моем коде есть проблема, которая вызывает проблемы с первой платой, но не со второй?

Прошу прощения, если что-то не понятно, и клянусь, я пытался понять ответы на другие подобные вопросы, но все они содержали несколько понятий или операторов / объектов или чего-либо еще, о чем я ничего не знаю. Если кто-то может указать на проблемы в моем коде и сказать мне, возможно ли решить вторую доску с его помощью или мне вообще нужен другой метод.

Заранее большое спасибо!

P.S .: Многие люди говорят об объектах в JavaScript или объектно-ориентированном программировании. Я не знаю, относится ли это к делу, но мы этого еще не видели.

Ответы [ 2 ]

0 голосов
/ 07 марта 2019

Что-то не так.Код, который вы опубликовали, решил «жесткую» плату за 1800 миллисекунд .После некоторой оптимизации я получил примерно 300 миллисекунд на том же ноутбуке с Windows, который использовался для тестирования.

Я предоставляю здесь оптимизированную версию, чтобы проверить, может ли компьютер с ней работать под управлением , если вы хотите попробовать .Я весьма осторожен с предложением сделать это, если вы все еще работаете над решением для перебора, но это, безусловно, версия вашего кода!

В конце вы можете проверить, является ли универсальный компьютер простоне позволяет алгоритмам перебора быть достаточным для выполнения времени (или реализует ограничение «шага» инструкции в пользовательском движке JS, который он запускает).

function emptyCells( board) {
    var empty = [];
    for (var i = 0; i < 9; i++) {
        for (var j = 0; j < 9; j++) {
            if (board[i][j] === 0) {
                var boxRow = 3* Math.floor( i/3);
                var boxCol = 3* Math.floor( j/3);
                empty.push([i,j, boxRow, boxCol]);
            }
        }
    }
    return empty;
}

function isUnique( board, empty, value) {
    var row, col;

    // test row
    row = board[empty[0]];
    for( col = 0; col < 9; ++ col) {
        if( value == row[col]) {
            return false;
        }
    }
    // test col
    col = empty[1];
    for( var row = 0; row < 9; ++row) {
        if( value == board[ row][col]){
            return false;
        }	
    }
    // test box
    var boxRow = empty[2];
    var boxCol = empty[3];
    for( var i = 3; i--;) {
        row = board[ boxRow++];
        for( var j = 3; j--;) {
            if( row[boxCol + j] == value) {
                return false;
            }
        }
    }
    return true;
}

var solve = function (board) {
    var empty = emptyCells( board);

    nextEmpty:
    for (var i = 0; i < empty.length;) { // We check every possible value for all empty 1x1 squares.
        var row = empty[i][0]; // Used for row and 3x3 square checks
        var column = empty[i][1]; // Used for column and 3x3 square checks
        var value = board[row][column] + 1; // We start at 1, because obviously 0 is not a Sudoku value.   
        var cell = empty[i];

        while (value <= 9) { // test values up to 9.
            if( isUnique( board, cell, value)) {
                board[row][column] = value; // We assign said value to the corresponding board 1x1 square, for now.
                i++; // Move on to the check next empty cell.
                continue nextEmpty;
            }
            value++; // If the value is invalid, we simply try the next possible value.    
        }

        board[row][column] = 0;
        if( i == 0) {  // board is not solvable
            return null;
        }
        i--; // We go back to the previous 1x1 square to try a different value.
    }
    return board;
};
var board = [
    [4,0,0,6,0,7,0,8,5],
    [0,0,0,0,0,0,6,0,0],
    [0,0,7,0,0,0,0,0,0],
    [0,5,0,0,0,3,0,0,4],
    [3,7,0,0,0,8,0,0,0],
    [6,0,0,2,0,0,0,0,0],
    [8,0,0,0,0,0,3,1,0],
    [0,3,1,0,4,9,0,0,0],
    [0,0,0,0,0,0,0,0,9]
];
var t0 = Date.now();
solve(board);
var t1 = Date.now();
console.log( " in " + (t1-t0) + "ms");
console.log( board.map( row=> row.join(',')).join('\n'));
console.log( "\n solved in " + (t1-t0) + "ms");
0 голосов
/ 07 марта 2019

Вторая проблема решается на моем компьютере через ~ 10 секунд

[4, 9, 3, 6, 1, 7, 2, 8, 5]
[2, 8, 5, 9, 3, 4, 6, 7, 1]
[1, 6, 7, 8, 5, 2, 4, 9, 3]
[9, 5, 8, 1, 6, 3, 7, 2, 4]
[3, 7, 2, 4, 9, 8, 1, 5, 6]
[6, 1, 4, 2, 7, 5, 9, 3, 8]
[8, 4, 9, 5, 2, 6, 3, 1, 7]
[5, 3, 1, 7, 4, 9, 8, 6, 2]
[7, 2, 6, 3, 8, 1, 5, 4, 9]
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...