数独生成器的递归解决方案

Posted

技术标签:

【中文标题】数独生成器的递归解决方案【英文标题】:Recursive solution to Sudoku generator 【发布时间】:2012-03-31 19:55:29 【问题描述】:

我正在尝试编写一种算法,以在 Java 或 javascript 中创建合法的数独板。两者都不起作用,我也不完全确定为什么。

本质上,这两个程序的问题是 x 或 y 的增量超出了应有的范围(跳过正方形)。我一生都无法弄清楚这是怎么发生的。如果需要,我可以提供完成 JS 解决方案的 html

我最好的猜测是它与我如何使用递归创建堆栈有关,但据我所知,它应该工作。 在我的旧代码中有一个不正确的 for 循环,我知道这一点。我贴的是旧版本,现在已经修复了。

Java:

import java.util.*;

public class SudokuGenerator

//credit:cachao
//http://***.com/questions/9959172/recursive-solution-to-sudoku-generator
public static final int BOARD_WIDTH = 9;
public static final int BOARD_HEIGHT = 9;

public SudokuGenerator() 
    board = new int[BOARD_WIDTH][BOARD_HEIGHT];

//Recursive method that attempts to place every number in a square
public int[][] nextBoard()

    nextBoard(0,0);
    return board;


public void nextBoard(int x, int y)

    int nextX = x;
    int nextY = y;
    //int[] toCheck = Collections.shuffle(Arrays.asList(1,2,3,4,5,6,7,8,9));
    int[] toCheck = 1,2,3,4,5,6,7,8,9;
    Collections.shuffle(Arrays.asList(toCheck));

    for(int i=0;i<toCheck.length;i++)
    
        if(legalMove(x, y, toCheck[i]))
        
            board[x][y] = toCheck[i];
            if(x == 8)
            
                if(y == 8)
                    break;//We're done!  Yay!
                else
                
                    nextX = 0;
                    nextY++;
                
            
            else
            
                nextX++;
            
            nextBoard(nextX, nextY);
        
    
    board[x][y] = 0;


public boolean legalMove(int x, int y, int current) 
    for(int i=0;i<9;i++) 
        if(current == board[x][i])
            return false;
    
    for(int i=0;i<9;i++) 
        if(current == board[i][y])
            return false;
    
    int cornerX = 0;
    int cornerY = 0;
    if(x > 2)
        if(x > 5)
            cornerX = 6;
        else
            cornerX = 3;
    if(y > 2)
        if(y > 5)
            cornerY = 6;
        else
            cornerY = 3;
    for(int i=cornerX;i<10 && i<cornerX+3;i++)
        for(int j=cornerY;j<10 && j<cornerY+3;j++)
            if(current == board[i][j])
                return false;
    return true;


public void print()

    for(int i=0;i<9;i++)
    
        for(int j=0;j<9;j++)
            System.out.print(board[i][j] + "  ");
        System.out.println();
    


public static void main(String[] args)

    SudokuGenerator sg = new SudokuGenerator();
    sg.nextBoard();
    sg.print(); 

int[][] board;

Javascript:

//Recursive method that attempts to place every number in a square
function driver()

    board = new Array(10);
    for(var i=0;i<9;i++)
        board[i] = new Array(10);
    nextBoard(0,0);
    print();


function nextBoard(x, y)

    var nextX = x;
    var nextY = y;
    for(var i=1;i<10;i++) 
        console.log(y + " " + x + " " + i);
        document.getElementById(y + " " + x).innerHTML = i;
        if(legalMove(x, y, i)) 
            board[x][y] = i;
            if(x === 8) 
                if(y === 8)
                    return board;//We're done!  Yay!
                else 
                    nextX = 0;
                    nextY++;
                
            
            else
                nextX++;
            nextBoard(nextX, nextY);
        
    
    //This is needed for legalMove to work, otherwise [x][y] == 9
    board[x][y] = undefined;


function legalMove(x, y, current) 
    for(var i=0;i<9;i++) 
        if(current === board[x][i])
            return false;
    
    for(var i=0;i<9;i++) 
        if(current === board[i][y])
            return false;
    
    var cornerX = 0;
    var cornerY = 0;
    if(x > 2)
        if(x > 5)
            cornerX = 6;
        else
            cornerX = 3;
    if(y > 2)
        if(y > 5)
            cornerY = 6;
        else
            cornerY = 3;
    for(var i=cornerX;i<10 && i<cornerX+3;i++)
        for(var j=cornerY;j<10 && j<cornerY+3;j++)
            if(current === board[i][j])
                return false;
    return true;


function print() 
    for(var i=0;i<9;i++)
        for(var j=0;j<9;j++)
        
            document.getElementById(i + " " + j).innerHTML = board[i][j];
            console.log(board[i][j]);           
        


var board;

【问题讨论】:

将代码放在问题中而不是粘贴箱中。 【参考方案1】:

在 Java 代码中: 我会把它翻译成伪代码:

for all z values:
    If for current (x,y), the number 'z' is legal then:
        insert z to current (x,y)
        if finished
            hooray!
        else 
            go to next square
    else try next number

但是如果你不能在里面放任何数字,因为它最终是非法的(也就是你不能在特定方格中插入任何数字的板)?

你没有解决这个问题。您需要做的是通过回溯实现它:

for all z values:
    If for current (x,y) the number 'z' is legal then:
        insert z to current (x,y)
        go to next(x,y)
            try to complete the board    // recursive call
        if you completed the board       // == the result of the recursion is legal
            return the completed board
    If all z values have been attempted
        return "cannot complete board"
    increment z, try again with current (x,y)

【讨论】:

我明白你在做什么,我明白。我以为我已经使用递归堆栈处理了它。如果方块 P 在 4 处是合法的,然后移动到方块 Q,那里没有任何东西是合法的,递归不会回到 P 然后在 5 处尝试吗? 它不会 - 你没有参与回溯的代码。问题的核心是一个决定:如果它有效,很好,否则,尝试下一个选项。您没有条款来处理“如果我的猜测是合法的,但我无法从那里解决它怎么办?” - 这基本上意味着你需要保存当前的棋盘,用第一个合法的猜测尝试它,如果没有解决方案,则将其视为错误猜测 我已按照您的建议进行了更改,但问题仍然存在。我仍然不完全明白为什么在递归调用失败后,函数停止运行。 您需要递归调用来接受 x 和 y 值、数字和要处理的板。这样,您只需发送带有合法猜测的棋盘的 .clone() (例如,如果您失败,则将正方形 0、0 标记为 -1)。首先执行结束条件,然后在开始时进行迭代(如果 x == 8,然后尝试使用 x == 0 和 y++ 等),然后执行以下操作: int[][] board = recursive call using this .clone() for next x, y... 如果 board[0][0] == -1,递归调用并递增值【参考方案2】:

Java:

    你应该初始化你的 board 变量,你可能想在构造函数中初始化它:

    public class SudokuGenerator 
    
        public static final int BOARD_WIDTH = 9;
        public static final int BOARD_HEIGHT = 9;
    
        public SudokuGenerator() 
            board = new int[BOARD_WIDTH][BOARD_HEIGHT];
        
    
    

    我相信你在函数 nextBoard 中的循环迭代器是错误的:

    for(int i=1;i&lt;10;i++) ...

    我认为你想从 0 迭代到 9。

    在函数nextBoard中,还需要检查变量:

    int[] toCheck = 1,2,3,4,5,6,7,8,9;

    你得到一个java.lang.ArrayIndexOutOfBoundsException,你应该把它从0初始化到8,否则你尝试访问板行号9,你会得到一个运行时错误。

    您需要解决的另一个问题是在nextBoard() 函数中将x 设置为9。使用以下参数“手动”调用函数nextBoard(int x, int y)nextBoard(7,3),您就会明白为什么 x 被设置为 9。具体检查变量nextX 的值。

我相信如果您使用debugger 来检查此类错误,它会真的对您有所帮助,here 您有一个很好的教程和视频解释(如果您使用的是 Eclipse IDE)。

希望对你有帮助。

【讨论】:

尝试检查此错误,然后发布更具体的问题。 我已经修复了这些错误。就像我说的,那是旧代码。如果您想解释如何将 x 和/或 y 设置为 9,那就太好了! 完美@SomeKittens。我现在已经更新了我的问题,希望对您有所帮助。【参考方案3】:

Java:

nextBoard 中的循环迭代器的范围是 1 到 9。我不认为你是这个意思。在函数 legalMove.... 中相同。将cornerX 和cornerY 初始化为0。

【讨论】:

哎呀,好像我粘贴了旧版本的代码。我会解决的。【参考方案4】:

有趣的问题,我刚刚注意到 Java 代码中的一个错误:对 Collection.shuffle() 的调用不是没有用,因为在此调用之后 toCheck 数组将保持未修改(未改组)吗?这是我的快速修复(我相信还有更聪明的方法可以做到):

List<Integer> lst = Arrays.asList(1, 2, 3, 4, 5, 6, 7, 8, 9);
Collections.shuffle(lst);
for (int i=0; i<lst.size(); i++)
    toCheck[i] = lst.get(i);

【讨论】:

哇,过去的爆炸。你是对的,当我在全班面前展示时,我们发现了这一点。尴尬。【参考方案5】:

在 Java 中,数组索引是从零开始的。在nextBoard 中,您循环遍历1..9 for i 并将其用作toCheck 的索引,这将跳过索引0 处的第一个元素并越过数组的末尾。如果到达包含toCheck[i] 的行且i 等于9,这将抛出ArrayIndexOutOfBoundsException

【讨论】:

我已经解决了这个问题,但问题仍然存在。看起来 x 和/或 y 以某种方式设置为 9,我不知道为什么。【参考方案6】:
import java.io.File;
import java.io.FileNotFoundException;
import java.util.Arrays;
import java.util.Scanner;
import java.util.logging.Level;
import java.util.logging.Logger;


public class SudokuNrupen 

     public static int[][] p;
     public static int tmp[] ;
     public static int tmp2D[][] ;


    public static void main(String[] args)

        tmp = new int[9];
        tmp2D = new int[3][3];
        p = new int[9][9];

         System.out.print("Enter the name of he file below ");
         Scanner scan = new Scanner (System.in);
         String name = scan.nextLine();
         File file = new File( name );

         if ( file.exists())   
            try 
                Scanner inFile = new Scanner( file );
                for(int i=0; i<9; i++)
                     for(int j=0; j<9; j++)
                         if(inFile.hasNextInt())
                             p[i][j] = inFile.nextInt();
                         
                     
                    
            inFile.close();
             catch (FileNotFoundException ex) 
                Logger.getLogger(SudokuNrupen.class.getName()).log(Level.SEVERE, null, ex);
            
       

      display(p);
      solve(p);
      System.out.println("Solved Sudoku is:");
      display(p);      


     

     public static void display(int [][] p)
    
        for(int i=0; i<p.length;i++)
        
            for(int j=0; j<p[i].length;j++)
            
                System.out.print("   ");
                if(p[i][j]<10)     System.out.print(p[i][j] + " ");
                else                    System.out.print(p[i][j]);
                System.out.print("  ");
            
            System.out.println();
            
      

    public static boolean solve(int [][] p)
    
        if(!isValidSudoku(p))
        
             return false;
        
        if(isComplete(p)==true)
        
            return true;
        
        for(int i=0; i<9; i++)
        
            for(int j=0 ; j<9 ; j++)
            
                if(p[i][j]==0) 
                
                    int k=1;
                    while(k<=9)
                    
                        p[i][j]=k;
                        if(solve(p))
                        
                            return true;
                        
                        else    k++;
                        
                    p[i][j]=0; 
                    return false; 
                
            
        
        return true;
    


    public static boolean isComplete(int [][]p)
    
        for(int i=0; i<9; i++)
        
            for(int j=0 ; j<9 ; j++)
            
                if(p[i][j]==0)
                    return false;
                
            
        
        return true;
        


    public static boolean isRepeated(int [] a)
    
        for(int i=0; i<8; i++)
        
            if((a[i]!=0 || a[i+1]!=0))
            
                     if(a[i]==a[i+1])
                         return true;
                     
              
        
        return false;    
    


 public static boolean isDuplicateEx0(int [][]p)
    

        for(int i=0; i<p[0].length; i++)
        
            for(int j=0 ; j<9 ; j++)
            
                tmp[j]=p[i][j];
            
            Arrays.sort(tmp);

            System.out.println(Arrays.toString(tmp));

            if(isRepeated(tmp)==true)
            
                System.out.println("Duplicates are found in row");
                return true;
            

        

        display(p);
        for(int j=0; j<p[0].length; j++)
        
            for(int i=0 ; i<9 ; i++)
            
                tmp[i]=p[i][j];
            
            Arrays.sort(tmp);

            if(isRepeated(tmp)==true)
            
                System.out.println("Duplicates are found in columns");
                return true;
            

        

        display(p);

        for(int z=0;z<9;z++)
            tmp[z]=0;
        

        int x=0,y=0;

        for(int i=0; i<3;i++)
           
            y=0;
            for(int j=0;j<3;j++)
            
                tmp2D[x][y]=p[i][j];
                y++;
            
            x++;
        
        for(int i=0; i<3; i++)
        
            for(int j=0; j<3; j++)
            
                tmp[(i*tmp2D.length) + j] = tmp2D[i][j];
            
        
        Arrays.sort(tmp);
        if(isRepeated(tmp)==true)
        
            return true;
        

        for(int z=0;z<9;z++)
            tmp[z]=0;
        
        x=0;
        y=0;

        for(int i=0; i<3;i++)
           
            y=0;
            for(int j=3;j<6;j++)
            
                tmp2D[x][y]=p[i][j];
                y++;
            
            x++;
        
        for(int i=0; i<3; i++)
        
            for(int j=0; j<3; j++)
            
                tmp[(i*tmp2D.length) + j] = tmp2D[i][j];
            
        
        Arrays.sort(tmp);
        if(isRepeated(tmp)==true)
        
            return true;
        


        for(int z=0;z<9;z++)
            tmp[z]=0;
        
        x=0;
        y=0;

        for(int i=0; i<3;i++)
           
            y=0;
            for(int j=6;j<9;j++)
            
                tmp2D[x][y]=p[i][j];
                y++;
            
            x++;
        
        for(int i=0; i<3; i++)
        
            for(int j=0; j<3; j++)
            
                tmp[(i*tmp2D.length) + j] = tmp2D[i][j];
            
        
        Arrays.sort(tmp);
        if(isRepeated(tmp)==true)
        
            return true;
        

        for(int z=0;z<9;z++)
            tmp[z]=0;
        
        x=0;
        y=0;

        for(int i=3; i<6;i++)
           
            y=0;
            for(int j=0;j<3;j++)
            
                tmp2D[x][y]=p[i][j];
                y++;
            
            x++;
        
        for(int i=0; i<3; i++)
        
            for(int j=0; j<3; j++)
            
                tmp[(i*tmp2D.length) + j] = tmp2D[i][j];
            
        
        Arrays.sort(tmp);
        if(isRepeated(tmp)==true)
        
            return true;
        

        for(int z=0;z<9;z++)
            tmp[z]=0;
        
        x=0;
        y=0;

        for(int i=3; i<6;i++)
           
            y=0;
            for(int j=3;j<6;j++)
            
                tmp2D[x][y]=p[i][j];
                y++;
            
            x++;
        
        for(int i=0; i<3; i++)
        
            for(int j=0; j<3; j++)
            
                tmp[(i*tmp2D.length) + j] = tmp2D[i][j];
            
        
        Arrays.sort(tmp);
        if(isRepeated(tmp)==true)
        
            return true;
        

        for(int z=0;z<9;z++)
            tmp[z]=0;
        
        x=0;
        y=0;

        for(int i=3; i<6;i++)
           
            y=0;
            for(int j=6;j<9;j++)
            
                tmp2D[x][y]=p[i][j];
                y++;
            
            x++;
        
        for(int i=0; i<3; i++)
        
            for(int j=0; j<3; j++)
            
                tmp[(i*tmp2D.length) + j] = tmp2D[i][j];
            
        
        Arrays.sort(tmp);
        if(isRepeated(tmp)==true)
        
            return true;
        

        for(int z=0;z<9;z++)
            tmp[z]=0;
        
        x=0;
        y=0;

        for(int i=6; i<9;i++)
           
            y=0;
            for(int j=0;j<3;j++)
            
                tmp2D[x][y]=p[i][j];
                y++;
            
            x++;
        
        for(int i=0; i<3; i++)
        
            for(int j=0; j<3; j++)
            
                tmp[(i*tmp2D.length) + j] = tmp2D[i][j];
            
        
        Arrays.sort(tmp);
        if(isRepeated(tmp)==true)
        
            return true;
        

        for(int z=0;z<9;z++)
            tmp[z]=0;
        
        x=0;
        y=0;

        for(int i=6; i<9;i++)
           
            y=0;
            for(int j=3;j<6;j++)
            
                tmp2D[x][y]=p[i][j];
                y++;
            
            x++;
        
        for(int i=0; i<3; i++)
        
            for(int j=0; j<3; j++)
            
                tmp[(i*tmp2D.length) + j] = tmp2D[i][j];
            
        
        Arrays.sort(tmp);
        if(isRepeated(tmp)==true)
        
            return true;
        

        for(int z=0;z<9;z++)
            tmp[z]=0;
        
        x=0;
        y=0;

        for(int i=6; i<9;i++)
           
            y=0;
            for(int j=6;j<9;j++)
            
                tmp2D[x][y]=p[i][j];
                y++;
            
            x++;
        
        for(int i=0; i<3; i++)
        
            for(int j=0; j<3; j++)
            
                tmp[(i*tmp2D.length) + j] = tmp2D[i][j];
            
        
        Arrays.sort(tmp);
        if(isRepeated(tmp)==true)
        
            return true;
        


        return false;
    



     public static boolean isValidSudoku(int [][] p)
     
           return (!isDuplicateEx0(p));  
     

【讨论】:

请更新您之前的答案,而不是创建一个全新的答案。

以上是关于数独生成器的递归解决方案的主要内容,如果未能解决你的问题,请参考以下文章

利用生成器yield 递归解决八王后问题

python 生成器和递归

为啥迭代置换生成器比递归慢?

如何快速生成一个数独

数字组合生成器

请问如何随机生成数独数据