Java 数独问题求解的模拟退火算法

Java 数独问题求解的模拟退火算法,java,algorithm,artificial-intelligence,sudoku,simulated-annealing,Java,Algorithm,Artificial Intelligence,Sudoku,Simulated Annealing,我试图用模拟退火算法解决一个9x9数独难题,但我的实现似乎不能正常工作。它甚至没有更接近一个低成本的解决方案,而是一直围绕着成本在60到80之间的结果进行循环 “我的成本函数”返回三项内容的总和:每行、列和块(3x3)中的重复位数 我实现的后继(邻居)函数用随机值更改9x9网格中随机选择的两个数字 下面是我的SA函数,它没有按预期工作: public static void simulatedAnnealing() { Sudoku neighbour; // candidate succes

我试图用模拟退火算法解决一个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 static void simulatedAnnealing(){
数独邻居;//候选后继对象
最终双温=2.0;//初始温度
最终双冷却系数=0.999;//冷却常数
final int maxIterations=1000;//迭代次数
对于(双t=温度;t>1;t*=冷却系数){
对于(int i=0;i
生成继任者的函数:

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;

}
公共数独生成接受器(){
int[]newGrid=newint[9][9];
对于(int o=0;o<9;o++){//克隆当前网格数组
对于(int g=0;g<9;g++){
新网格[o][g]=网格[o][g];
}
}
数独rndm=新数独(newGrid);//随机数独对象。
对于(int i=0;i<2;i++){//将在9x9网格中随机分配2个单元格。
int rndmCell=rndmValue();//用于随机化的随机数字。
int randomRow=rndm();//将被随机化的随机行
int randomCol=rndm();//将随机化的随机列
//防止数独中给定的单元格随机化(在问题定义中)
布尔值shouldContinue=false;
用于(坐标c:SudokuSolver.concrete坐标){
如果(c.row==randomRow&&c.col==randomCol){
shouldContinue=true;
打破
}
}
如果(应该继续){
我--;
继续;
}
//预防结束。
rndm.grid[randomRow][randomCol]=rndmCell;
}
返回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;
}
public int cost(){
如果(hasZeros()){//如果网格中没有完全填充数字,则不计算其成本。
返回-1;
}
整数成本=0;
对于(int i=0;i<9;i++){//查找行和列中的总合谋。
cost+=FindNumberOfcolusions(网格[i]);//在“i”行查找共谋。
cost+=FindNumberOfcolusions(getColumn(grid,i));//在列“i”中查找共谋。
}
对于(int r=0;r<9;r+=3){//在块(3x3)中查找总colusions。
对于(int c=0;c<9;c+=3){
int[]块=新的int[9];
int ctr=0;
对于(int i=r;i

当我运行该程序时,输出成本在60到80之间。在此之后,温度低于极限,程序输出的解决方案的成本约为该时间间隔。谁能告诉我我做错了什么?提前感谢。

我也有一个与您描述的问题类似的问题,我的适应能力仍然很差(实际上,我的问题是没有用Python复制列表)。我真的不能确定为什么代码会被卡住,但如果我不得不猜测的话:邻居代(
int-rndmCell=rndmValue();int-randomRow=rndm();int-randomCol=rndm();
)实际上可能弊大于利。假设您有一个几乎完整的数独游戏,但突然间,您现在拥有的两个正确的单元格将其值更改为完全相反的值,这不仅在单元格本身上是错误的,而且在行、列和/或3x3正方形上也是错误的。我不是数学家,但逻辑告诉我,数独越适合(也就是说,它的适合度越接近0),就越有可能通过随机改变单元格来搞乱数独。这种方法很容易让你陷入局部极小值


对于这个问题,一个更“知情”的解决方案是保持数独游戏的三个基本限制之一不变,例如,生成值排列的行
[1..9]
,交换随机行的两个单元格(因此仍然满足限制),并且只计算柱和3x3正方形上的适合度。这种邻居代的选择通常更有效。如果你感兴趣,这个想法来自报纸。我可以说这个想法对我帮助很大,现在算法完成了我提供的数独:)

我也有一个与您描述的问题类似的问题,我的适应能力仍然很差(实际上,我的问题是没有用Python复制列表)。我真的不能确定为什么代码会被卡住,但如果我不得不猜测的话:邻居代(
int-rndmCell=rndmValue();int-randomRow=rndm();int-randomCol=rndm();
)实际上可能弊大于利。假设您有一个几乎完整的数独游戏,但突然间,您现在拥有的两个正确的单元格将其值更改为完全相反的值,这不仅在单元格本身上是错误的,而且在行、列和/或3x3正方形上也是错误的。我不是数学家,但逻辑告诉我,数独越适合(也就是说,它的适合度越接近0),就越有可能通过随机改变单元格来搞乱数独。这种方法可能会使您陷入局部最小easi