Имитация отжига для решения судоку

Я пытаюсь решить головоломку судоку 9x9 с помощью имитации отжига, но моя реализация, похоже, работает неправильно. Он даже не приближается к более дешевому решению, а вместо этого продолжает вращаться вокруг результатов, которые стоят от 60 до 80.

Моя функция стоимости возвращает сумму трех вещей: количество повторяющихся цифр в каждой строке, столбце и блоке (3x3).

И функция преемника (соседка), которую я реализовал, заменяет две случайно выбранные цифры из сетки 9x9 случайными значениями.

И вот моя функция SA, которая не работает должным образом:

public static void simulatedAnnealing() {

Sudoku neighbour; // candidate successor object
final Double temperature = 2.0; // initial temperature
final Double coolingFactor = 0.999; // cooling constant
final int maxIterations = 1000; // number of iterations

for(Double t = temperature; t>1; t*=coolingFactor) {

    for(int i = 0; i < maxIterations; i++) {

        neighbour = sudoku.generateSuccessor(); // set random neighbour
        int delta = neighbour.cost() - sudoku.cost(); // calculate delta

        if (delta <= 0) {
            sudoku = neighbour; // always accept good step.
         } else {
               if (Math.exp(-delta / temperature) > Math.random()) { // Simulated annealing
                   sudoku = neighbour;
               } 
         } 
     }

    System.out.println(sudoku.cost());
    if(sudoku.cost() == 0) { break; } // if puzzle is solved

} }

Функция для генерации преемников:

public Sudoku generateSuccessor() {

int[][] newGrid = new int[9][9];

for(int o = 0; o < 9; o ++) { // cloning current grid array
    for(int g = 0; g < 9; g ++) {
        newGrid[o][g] = grid[o][g];
     }
 }

Sudoku rndm = new Sudoku(newGrid); // random Sudoku object.

for (int i = 0; i < 2; i++) { // will randomize 2 cells in 9x9 grid.

    int rndmCell = rndmValue(); // random digit for randomizing.
    int randomRow = rndm(); // random row that will be randomized
    int randomCol = rndm(); // random column that will be randomized

    // prevent randomizing given cells in sudoku (in problem definition)
    boolean shouldContinue = false;
    for (Coordinate c : SudokuSolver.concreteCoordinates) {
        if (c.row == randomRow && c.col == randomCol) { 
            shouldContinue = true;
            break;
        }
    }
    if (shouldContinue) {
        i--;
        continue;
    }
    // prevention end.

    rndm.grid[randomRow][randomCol] = rndmCell;
}

return rndm;

}

Функция стоимости:

public int cost() {
    if(hasZeros()) { // if grid is not totally filled with numbers don't calculate its cost.
        return -1;
    }

    int cost = 0;
    for(int i = 0; i< 9; i++) { // find total collusions in rows&columns.
        cost += findNumberOfCollusions(grid[i]); // find collustions at row 'i'.
        cost += findNumberOfCollusions(getColumn(grid,i)); // find collustions at column 'i'.
    }

    for(int r = 0; r < 9; r += 3) { //find total colusions in blocks (3x3).
        for(int c = 0; c < 9; c += 3) {
            int[] block = new int[9];
            int ctr = 0;
            for (int i = r; i < r + 3; i++) {
                for (int y = c; y < c+ 3; y++) {
                    block[ctr] = grid[i][y];
                    ctr++;
                }
            }
            cost += findNumberOfCollusions(block);
        }
    }
    return cost;
}

Когда я запускаю программу, стоимость вывода составляет от 60 до 80. После этого температура опускается ниже предела, и программа выводит решение, которое стоит около этого интервала. Может ли кто-нибудь сказать мне, что я делаю неправильно? Заранее спасибо.


person Muhammed Gül    schedule 02.05.2020    source источник
comment
Пожалуйста, опубликуйте полное и минимальное решение. Без реализации функций типа generateSuccessor сложно сказать.   -  person ldog    schedule 02.05.2020
comment
@ldog Я отредактировал вопрос, спасибо.   -  person Muhammed Gül    schedule 02.05.2020


Ответы (1)


У меня также была проблема, аналогичная той, которую вы описываете, моя физическая форма оставалась зависшей (хотя на самом деле моя проблема заключалась в том, что я не копировал списки в Python). Я не могу точно сказать, почему ваш код зависает, но если бы мне пришлось угадывать: поколение соседей (int rndmCell = rndmValue(); int randomRow = rndm(); int randomCol = rndm();) может на самом деле приносить больше вреда, чем пользы. Представьте, что у вас есть почти полная судоку, но ни с того ни с сего две правильные ячейки, которые у вас были, теперь меняют свое значение на совершенно противоположное, что неверно не только в самой ячейке , но и в строке, столбец и/или квадрат 3x3. Я не математик, но логика подсказывает мне, что чем больше подходит судоку (то есть чем ближе его пригодность к 0), тем больше шансов испортить судоку случайным изменением ячеек. Этот подход может легко привести к тому, что вы застрянете на локальном минимуме.

Более информированное решение этой проблемы состояло бы в том, чтобы сохранить одно из трех основных ограничений головоломки судоку фиксированным, например, генерируя строки, которые являются перестановками значений [1..9], заменяя местами две ячейки случайной строки (таким образом, все еще выполняя ограничение ), и вычисление пригодности только для столбцов и квадратов 3x3. Такой выбор соседнего поколения обычно более эффективен. Если вам интересно, эта идея взята из статьи Метаэвристика может решать головоломки судоку. Могу сказать, что эта идея мне много помогла и теперь алгоритм доделывает судоку, который я предоставляю :)

person Le Sir Dog    schedule 17.10.2020