Алгоритм решения логики (для судоку в Java)

У меня проблемы с моим алгоритмом решения логики. Он решает головоломки с большим количеством подсказок очень хорошо, у него просто проблемы с головоломками, которые имеют менее 45 подсказок.

Это алгоритм для решения. Immutable - это логическое значение, которое определяет, можно ли изменить это значение. cell [row] [col].possibleValues ​​- это LinkedList в классе SudokuCell, который сохраняет значения, которые возможны для этого элемента сетки. grid.sGrid является основным массивом int [] [] головоломки. removeFromCells() - метод, который удаляет значения из строки, столбца и квадранта сетки. Этот код предоставляется ниже.

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

public boolean solve(){

    for(int i = 0; i < 81; i++){
        for(int row = 0; row < 9; row++){
            for(int col = 0; col < 9; col++){
                if(!immutable[row][col]){
                    if(cell[row][col].getSize() == 1){
                        int value = cell[row][col].possibleValues.get(0);
                        grid.sGrid[row][col] = value;
                        immutable[row][col] = true;
                        removeFromCells(row, col, value);
                    }
                }
            }
        }
    }


    int i = 0;
    for(int row = 0; row < 9; row++){
        for(int col = 0; col < 9; col++){
            if(grid.sGrid[row][col] == 0){
                i++;
            }
        }
    }

    if(i != 0){
        return false;
    } else{
        return true;
    }
}

Это код для удаленияFromCells()

Я думаю, что большая часть кода довольно понятна. Первый цикл for удаляет значение из строки и столбца (x, y), а второй цикл удаляет значение из квадранта.

public void removeFromCells(int x, int y, int value){

    /*
     * First thing to do, find the quadrant where cell[x][y] belong.
     */

    int topLeftCornerRow = 3 * (x / 3) ;
    int topLeftCornerCol = 3 * (y / 3) ;

    /*
     * Remove the values from each row and column including the one
     * where the original value to be removed is.
     */
    for(int i = 0; i < 9; i++){
        cell[i][y].removeValue(value);
        cell[x][i].removeValue(value);
    }


    for(int row = 0; row < 3; row++){
        for(int col = 0; col < 3; col++){
            cell[topLeftCornerRow + row][topLeftCornerCol + col].removeValue(value);
        }
    }
}

Другое место проблемы может быть, где построены возможные значения. Это метод, который у меня есть для этого:

Первый цикл for создает новые SudokuCells, чтобы избежать исключения ужасного нулевого указателя.

Любые нулевые значения в sGrid представлены как 0, поэтому цикл for пропускает их.

Конструктор для SudokuBoard вызывает этот метод, поэтому я знаю, что он вызывается.

public void constructBoard(){

    for(int row = 0; row < 9; row++){
        for(int col = 0; col < 9; col++){
            cell[row][col] = new SudokuCell();
        }
    }

    immutable = new boolean[9][9];

    for(int row = 0; row < 9; row++){
        for(int col = 0; col < 9; col++){
            immutable[row][col] = false;
            if(grid.sGrid[row][col] != 0){
                removeFromCells(row, col, grid.sGrid[row][col]);
                immutable[row][col] = true;
            }
        }
    }
}

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

Ответ 1

Кажется, вы построили только упрощенное решение, основанное на разрешении. Вам нужен полный откат, чтобы решать головоломки с меньшими намеками. Есть некоторые случаи, которые вы не можете решить без возврата.

В качестве альтернативы вам следует попытаться реализовать алгоритм Кнута (Dancing Links) для решения таких проблем. Это сложнее понять и реализовать, чем алгоритм обратного отслеживания, но он работает лучше:). См. Здесь: http://en.wikipedia.org/wiki/Dancing_Links

Это также алгоритм для более общей проблемы, и он был применен к решению судоку довольно успешно.

В wikipedia есть ссылка на статью, в которой подробно описывается, какие типы экземпляров можно решить с помощью программирования ограничений: http://4c.ucc.ie/~hsimonis/sudoku.pdf (найдено из здесь: http://en.wikipedia.org/wiki/Sudoku_algorithms). Таблица 4 действительно интересна:).

Ответ 2

Я использовал множество таких правил для разработки моего решения sudoku. Тем не менее я всегда оказывался вынужденным использовать откат для очень трудного судоку. Согласно википедии, некоторые судоку фактически невозможно решить, используя только правила.

Я выполнил в общей сложности 6 правил.

  • Никакое другое значение не допускается.
  • Некоторое значение разрешено в любом другом квадрате в том же разделе.
  • Некоторое значение разрешено в любом другом квадрате в той же строке или столбце.
  • Определенное значение разрешено только для одного столбца или строки внутри раздела, поэтому мы можем исключить это значение из этой строки или столбца в других разделах.
  • Голые пары
  • Линии

Я описал весь алгоритм и дал код в этих двух блогах (исходная версия использовала только первые 4 правила).

http://www.byteauthor.com/2010/08/sudoku-solver/

http://www.byteauthor.com/2010/08/sudoku-solver-update/

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