【问题标题】:Filling array with random digits in range用范围内的随机数字填充数组
【发布时间】:2016-07-01 16:28:46
【问题描述】:

我正在编写一个数独棋盘游戏生成器。我的代码如下所示:

/**
 * Created by szubansky on 3/9/16.
 */

public class SudokuBoard {

    static int N = 9;
    static int[][] grid = new int[N][N];

    static void printGrid()
    {
        for (int row = 0; row < N; row++)
        {
            for (int col = 0; col < N; col++) {
                System.out.printf("%5d", grid[row][col]);
            }
            System.out.println("\n");
        }
    }

    private static boolean checkRow(int row, int num)
    {
        for( int col = 0; col < 9; col++ )
            if(grid[row][col] == num)
                return false;

        return true;
    }

    private static boolean checkCol(int col, int num)
    {
        for( int row = 0; row < 9; row++ )
            if(grid[row][col] == num)
                return false;

        return true;
    }

    private static boolean checkBox(int row, int col, int num)
    {
        row = (row / 3) * 3;
        col = (col / 3) * 3;

        for(int r = 0; r < 3; r++)
            for(int c = 0; c < 3; c++)
                if(grid[row+r][col+c] == num)
                    return false;

        return true;
    }

    public static boolean fillBoard(int row, int col, int[][] grid)
    {
        if(row==9) {
            row = 0;
            if (++col == 9)
                return true;
        }

        if(grid[row][col] != 0)
            return fillBoard(row+1, col, grid);
        for(int num=1 + (int)(Math.random() * ((9 - 1) + 1)); num<=9; num++)
        {
            if(checkRow(row,num) && checkCol(col,num) && checkBox(row,col,num)){
                grid[row][col] = num;
                if(fillBoard(row+1, col, grid))
                    return true;
            }
        }
        grid[row][col] = 0;
        return false;
    }

    static public void main(String[] args){
        fillBoard(0, 0, grid);
        printGrid();
    }
}

问题在于在 fillBoard 回溯算法中生成数字 它基本上到处都是 0 当我将 for 循环中的 num 范围更改为 10 时,它会顺利进行,但我的数字需要低于 9 我还可以将回溯填充板的开头更改为row==8col==8,并用随机数正确填充它,最后一行和最后一列为“0”。 如何生成从 1 到 9 的随机数并填充我的所有网格?

【问题讨论】:

    标签: java arrays random


    【解决方案1】:

    试试这个:

    public static void main(String[] args) {
        int[][] grid = new int[9][9];
        randomFillGrid(grid, 1, 10);
        for (int[] row : grid) {
            System.out.println(Arrays.toString(row));
        }
    }
    
    static void randomFillGrid(int[][] grid, int randomNumberOrigin, int randomNumberBound) {
        PrimitiveIterator.OfInt iterator = ThreadLocalRandom.current()
                .ints(randomNumberOrigin, randomNumberBound)
                .iterator();
        for (int[] row : grid) {
            for (int i = 0; i < row.length; i++) {
                row[i] = iterator.nextInt();
            }
        }
    }
    

    编辑

    如果你想生成一个数独网格,即

    同一个整数不能在同一行、同一列或 9x9 棋盘的九个 3×3 子区域中的任何一个中出现两次。

    import java.util.Arrays;
    import java.util.Random;
    import java.util.concurrent.ThreadLocalRandom;
    
    /**
     * @author FaNaJ
     */
    public class SudokuGenerator {
    
        private static final int N = 9;
        private static final int S_N = 3;
    
        public static int[][] generateSudokuGrid() {
            int[][] grid = new int[N][N];
            int[] row = {1, 2, 3, 4, 5, 6, 7, 8, 9};
    
            for (int y = 0; y < N; y++) {
                int attempts = 0;
    
                do {
                    if (++attempts > 1000000) { // Oops! I know (sometimes :) it's not a good algorithm...
                        return generateSudokuGrid();
                    }
                    shuffleArray(row);
                } while (!isAllowed(grid, y, row));
    
                System.arraycopy(row, 0, grid[y], 0, N);
            }
    
            return grid;
        }
    
        static boolean isAllowed(int[][] grid, int y, int[] row) {
            // check columns
            for (int i = 0; i < y; i++) {
    
                for (int j = 0; j < N; j++) {
    
                    if (grid[i][j] == row[j]) {
                        return false;
                    }
    
                }
    
            }
    
            // check sub grids
            int startY = (y / S_N) * S_N;
    
            for (int x = 0; x < N; x++) {
                int startX = (x / S_N) * S_N;
                for (int j = startX; j < startX + S_N; j++) {
                    if (j != x) {
    
                        for (int i = startY; i < y; i++) {
    
                            if (grid[i][j] == row[x]) {
                                return false;
                            }
    
                        }
    
                    }
                }
            }
    
            return true;
        }
    
        static void shuffleArray(int[] array) {
            Random random = ThreadLocalRandom.current();
            for (int i = N; i > 1; i--) {
                swap(array, i - 1, random.nextInt(i));
            }
        }
    
        static void swap(int[] array, int i, int j) {
            int tmp = array[i];
            array[i] = array[j];
            array[j] = tmp;
        }
    
        public static void main(String[] args) {
            int[][] grid = generateSudokuGrid();
            for (int[] row : grid) {
                System.out.println(Arrays.toString(row));
            }
        }
    
    }
    

    输出:

    [3, 4, 6, 9, 1, 2, 7, 8, 5]
    [9, 7, 2, 3, 8, 5, 4, 1, 6]
    [5, 8, 1, 6, 7, 4, 3, 2, 9]
    [7, 6, 3, 8, 2, 9, 1, 5, 4]
    [4, 9, 5, 1, 6, 7, 2, 3, 8]
    [2, 1, 8, 4, 5, 3, 6, 9, 7]
    [6, 2, 4, 5, 9, 1, 8, 7, 3]
    [8, 5, 7, 2, 3, 6, 9, 4, 1]
    [1, 3, 9, 7, 4, 8, 5, 6, 2]
    

    【讨论】:

      【解决方案2】:

      您可以使用java.utilpackage 的Random 类来生成随机数。下面是一个生成 1 到 9 之间随机数的示例:

      Random rn = new Random();
      int answer = rn.nextInt(9) + 1;
      

      【讨论】:

      • 它确实会生成随机数,但每次调用 fillBoard() 时我都需要我的数独板是唯一的。它必须满足数独的要求
      • 你可以围绕这个包裹你的逻辑来检查生成的数字是否符合要求,如果没有,你可以重新生成它。 do..while 循环之类的东西会有所帮助。
      • @AndrzejStempnik 你没有在你的问题中包含这个。如果您的要求不断变化,您就不能指望人们正确回答..
      【解决方案3】:

      在我开始之前,让我说一下,虽然我不会直接告诉你所有的错误是什么,但我会帮助你缩小搜索范围并建议一种方法来进一步调试你的代码。

      现在你的问题是:你的数组只填充了零。

      只有两行可以填充您的数组。 第一个在数组中设置一个点,使其具有介于 1 和 9 之间的随机 int 值。如果这个随机数没有通过一系列测试,那么数组中的那个点就会被设置为零。

      如果其中一个测试器从未返回 true,则数组将仅填充零。

      这留下了 4 个可能被窃听的函数。 -checkRow -checkCol -复选框 -fillBoard

      前 2 个函数相对简单,所以我可以肯定地说它们工作正常。

      这只会导致 checkBox 和 fillBoard 函数被怀疑是您的错误的原因。

      至此,调试进入。

      这两个函数都包含循环。

      查看循环是否正常工作的一种方法是将一组预期更改/返回值与实际从程序中获得的值进行比较。

      为此,请使用调试器 (assert 语句 + java -ea program.java 和/或其他调试器方法)

      或者,您可以在循环中添加打印语句,用于打印各种变量/函数的获取值。

      无论如何,欢迎来到 Stacks Overflow。

      【讨论】:

        【解决方案4】:

        您提到您正在使用回溯算法,所以我认为使用回溯算法并尝试演示它是什么会很有趣。下面是您的代码和我的代码的混搭。我试图坚持你正在做的事情,只添加/更改我觉得我需要做的事情。

        import java.util.ArrayList;
        import java.util.Collections;
        import java.util.List;
        
        /**
         * SudokuBoard.
         */
        public class SudokuBoard {
        
        
            // Dimension size of everything
            static int N = 9;
            // Sudoku grid
            static int[][] grid = new int[N][N];
            // Values that are potentially valid at each position in the sudoku grid
            static int[][][] values = new int[N][N][N];
            // Current index into the values
            static int[][] index = new int[N][N];
        
            /**
             * Return a shuffled list of values from 1 - 9 with each value       
             * appearing only once.
             */
            private static List<Integer> getValidValues(){
                List<Integer> validValues = new ArrayList<>();
                for(int i = 1; i < 10; i++){
                    validValues.add(i);
                }
                Collections.shuffle(validValues);
                return validValues;
            }
        
            /**
             * Populate the values array with shuffled values to choose from.
             */
            private static void initValues()
            {
                for(int i = 0; i < values.length; i++){
                    for(int j = 0; j < values[i].length; j++){
                        List<Integer> validValues = getValidValues();
                        for(int k = 0; k < values[j].length; k++){
                            values[i][j][k] = validValues.get(k);
                        }
                    }
                }
            }
        
            /**
             * print the 2D sudoku grid.
             */
            public static void printGrid()
            {
                for (int row = 0; row < N; row++)
                {
                    for (int col = 0; col < N; col++) {
                        System.out.printf("%5d", grid[row][col]);
                    }
                    System.out.println("\n");
                }
            }
        
            /**
             * Check the row for validity.
             */
            private static boolean checkRow(int row, int num)
            {
                for( int col = 0; col < 9; col++ )
                    if(grid[row][col] == num)
                        return false;
        
                return true;
            }
        
            /**
             * Check the col for validity.
             */
            private static boolean checkCol(int col, int num)
            {
                for( int row = 0; row < 9; row++ )
                    if(grid[row][col] == num)
                        return false;
        
                return true;
            }
        
            /**
             * Check the box for validity.
             */
            private static boolean checkBox(int row, int col, 
                                            int num, boolean testRowCol)
            {
                int theR = row;
                int theC = col;
        
                row = (row / 3) * 3;
                col = (col / 3) * 3;
        
                for(int r = 0; r < 3; r++) {
                    for (int c = 0; c < 3; c++) {
                        if (testRowCol) {
                            if (theR == row + r && theC == col + c){
                                continue;
                            }
                        }
                        if (grid[row + r][col + c] == num) {
                            return false;
                        }
                    }
                }
        
                return true;
            }
        
            /**
             * Build the sudoku board.
             */
            public static boolean fillBoard(int row, int col)
            {
                // if we are back at the beginning then success!
                // but just for sanity we will check that its right.
                if(row != 0 && col != 0){
                    if(row % 9 == 0 && col % 9 == 0){
                        return checkBoard();
                    }
                }
        
                // don't go out of range in the grid.
                int r = row % 9;
                int c = col % 9;
        
                // get the index in the values array that we care about
                int indexIntoValues = index[r][c];
        
                // if the index is out of range then we have domain wipe out!
                // lets reset the index and try to back up a step. Backtrack!
                if(indexIntoValues > 8){
                    index[r][c] = 0;
                    // there are a few cases to cover
        
                    // if we are at the beginning and the index is out
                    // of range then failure. We should never get here.
                    if(row == 0 && col == 0) {
                        return false;
                    }
        
                    grid[r][c] = 0;
        
                    // if the row is at 0 then back the row up to row - 1 and 
                    // the col - 1
                    if(r == 0 && c > 0) {
                        return fillBoard(row - 1, col - 1);
                    }
        
                    // if the row is greater than 0 then just back it up by 1
                    if(r > 0){
                        return fillBoard(row - 1, col);
                    }
                }
                index[r][c] += 1;
        
                // get the value that we care about
                int gridValue = values[r][c][indexIntoValues];
        
                // is this value valid
                if(checkRow(r,gridValue) && checkCol(c,gridValue) && 
                   checkBox(r,c,gridValue,false)){
                    // assign it and move on to the next one
                    grid[r][c] = gridValue;
                    return fillBoard(row+1, r == 8 ? col + 1 : col);
                }
                // the value is not valid so recurse and try the next value
                return fillBoard(row, col);
            }
        
            /**
             * This is a sanity check that the board is correct.
             * Only run it after a solution is returned.
             */
            private static boolean checkBoard(){
        
                //the grid is N X N so just use N as the condition.
        
                //check rows are ok.
                for(int i = 0; i < N; i++){
                    for(int j = 0; j < N; j++){
                        //for each of the elements in the row compare against
                        //every other one except its self.
                        int toTest = grid[i][j];
                        //check that the digits in the elements are in the valid range.
                        if(toTest > 9 || toTest < 1)
                            return false;
                        for(int k = 0; k < N; k++){
                            //don't test me against myself
                            if(k == j)
                                continue;
                            //if i am equal to another in the row there is an error.
                            if(toTest == grid[i][k])
                                return false;
                        }
                    }
                }
        
                //check the cols are ok.
                for(int i = 0; i < N; i++){
                    for(int j = 0; j < N; j++){
                        //flip i and j to go for cols.
                        int toTest = grid[j][i];
                        for(int k = 0; k < N; k++){
                            if(k == j)
                                continue;
                            if(toTest == grid[k][i])
                                return false;
                        }
                    }
                }
        
                //check blocks are ok
                for(int i = 0; i < N; i++){
                    for(int j = 0; j < N; j++){
                        int toTest = grid[i][j];
                        if(!checkBox(i, j, toTest, true))
                            return false;
                    }
                }
                return true;
            }
        
            static public void main(String[] args){
                initValues();
                if(fillBoard(0, 0))
                    printGrid();
                else
                    System.out.println("Something is broken");
            }
        }
        

        所以我添加的主要内容是两个多维数组,一个是值列表,另一个是该值列表的当前索引。因此values 数组保留了每个单元格可以拥有的所有有效值的列表。我对这些值进行了洗牌,以给出您正在寻找的随机感觉。出于任何性能原因,我们并不真正关心这里的值排序,但我们希望它是“随机的”,以便在每次运行中获得不同的电路板。接下来,index 数组跟踪您在values 数组中的位置。在index 数组中的索引超出范围的任何时候,这意味着所有值都不起作用。这就是您需要将索引重置为零并返回到您来自的单元格以在那里尝试新值的点。这就是回溯步骤。我像你一样继续在数组中移动,但这不是必需的。我认为您实际上可以转到具有最少有效值的变量,因此在这种情况下索引最高,并且搜索应该更快,但是现在谁在乎,这是一个小型搜索,深度为 81 而您赢!我还包括了一个完整的检查板是否有效。

        这是一个输出:

        1    3    2    5    7    6    4    9    8
        
        7    5    8    1    9    4    3    2    6
        
        6    4    9    8    2    3    1    7    5
        
        8    6    3    2    5    9    7    1    4
        
        5    7    4    6    3    1    9    8    2
        
        9    2    1    4    8    7    5    6    3
        
        4    9    7    3    6    8    2    5    1
        
        3    8    5    7    1    2    6    4    9
        
        2    1    6    9    4    5    8    3    7
        

        我希望这对您有所帮助,这是一个有趣的。

        【讨论】:

          猜你喜欢
          • 2021-06-20
          • 2020-07-16
          • 1970-01-01
          • 1970-01-01
          • 2018-12-25
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2014-11-20
          相关资源
          最近更新 更多