1

I'm trying to solve a 9x9 sudoku puzzle using Simulated Annealing, but my implementation doesn't seem to be working correctly. It does not even get closer to a lower-cost solution but instead keeps circling around results that cost between 60 and 80.

My cost function returns the sum of three things: Number of repeating digits in each row, column and block (3x3).

And the successor (neighbour) function i implemented changes two randomly selected digits from the 9x9 grid with random values.

And here is my SA function that doesn't work as expected:

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

} }

Function for generating successors:

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;

}

Cost function:

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;
}

When i run the program the output is costs between 60 and 80. After that the temperature goes below the limit and the program outputs a solution that costs around that interval. Can anyone tell me what am i doing wrong? Thanks in advance.

Muhammed Gül
  • 825
  • 10
  • 19

1 Answers1

0

I also had a similar problem to the one you describe, my fitness remained stuck (actually though, my problem was with not copying lists in Python). I can't really assure why your code gets stuck, but if I had to guess: the neighbor generation (int rndmCell = rndmValue(); int randomRow = rndm(); int randomCol = rndm();) may be actually doing more harm than good. Imagine that you have a nearly complete sudoku, but out of the blue two of the correct cells that you had now change their value to a complete opposite one, which is not only wrong on the cell itself but also on the row, column and/or 3x3 square. I'm no mathematician, but logic tells me that the more fitting the sudoku is (i.e. the closer its fitness is to 0), the more chances there are to mess up the sudoku by randomly changing cells. This approach may get you stuck on a local minimum easily.

A more "informed" solution for this problem would be to keep one of the three basic restrictions of the sudoku puzzle fixed by, for instance, generating rows that are permutations of the values [1..9], swapping two cells of a random row (thus still fulfilling the restriction), and calculating the fitness only on the columns and on the 3x3 squares. This choice of neighbor generation is usually more effective. If you are interested, this idea comes from the paper Metaheuristics can solve Sudoku puzzles. I can say that this idea helped me a lot and now the algorithm completes sudokus that I provide :)

Le Sir Dog
  • 95
  • 1
  • 10