【问题标题】:Brute force Algorithm for creation of Sudoku Board用于创建数独板的蛮力算法
【发布时间】:2010-08-28 00:41:40
【问题描述】:

我正在开发的是,最初整个数独板都是空的。 随机单元之一(81 个)填充了随机值(1-9)。

现在我想使用蛮力方法填充所有剩余的单元格。
谷歌搜索后我了解到,我们应该从第一个单元格开始并用 1 填充它(如果它有效),然后用 2 填充第二个单元格(如果它有效,我们将开始检查一个大于最后填充的单元格,在本例中为 1,一旦达到 9,我们将其重置为 1)。

问题是它不能正常工作!

谁能给我链接到确切的算法。

【问题讨论】:

    标签: c# algorithm sudoku


    【解决方案1】:

    这是回溯方法的一个实现:

    import java.util.Random;
    
    public class Sudoku {
    
        public static void main(String[] args) {
            Random rand = new Random();
            int r = rand.nextInt(9);
            int c = rand.nextInt(9);
            int value = rand.nextInt(9) + 1;
            Board board = new Board();
            board.set(r, c, value);
            System.out.println(board);
            solve(board, 0);
            System.out.println(board);
        }
    
        private static boolean solve(Board board, int at) {
            if (at == 9*9)
                return true;
            int r = at / 9;
            int c = at % 9;
            if (board.isSet(r, c))
                return solve(board, at + 1);
            for (int value = 1; value <= 9; value++) {
                if (board.canSet(r, c, value)) {
                    board.set(r, c, value);
                    if (solve(board, at + 1))
                        return true;
                    board.unSet(r, c);
                }
            }
            return false;
        }
    
        static class Board {
            private int[][] board = new int[9][9];
            private boolean[][] rs = new boolean[9][10];
            private boolean[][] cs = new boolean[9][10];
            private boolean[][][] bs = new boolean[3][3][10];
            public Board() {}
            public boolean canSet(int r, int c, int value) {
                return !isSet(r, c) && !rs[r][value] && !cs[c][value] && !bs[r/3][c/3][value];
            }
            public boolean isSet(int r, int c) {
                return board[r][c] != 0;
            }
            public void set(int r, int c, int value) {
                if (!canSet(r, c, value))
                    throw new IllegalArgumentException();
                board[r][c] = value;
                rs[r][value] = cs[c][value] = bs[r/3][c/3][value] = true;
            }
            public void unSet(int r, int c) {
                if (isSet(r, c)) {
                    int value = board[r][c];
                    board[r][c] = 0;
                    rs[r][value] = cs[c][value] = bs[r/3][c/3][value] = false;
                }
            }
            public String toString() {
                StringBuilder ret = new StringBuilder();
                for (int r = 0; r < 9; r++) {
                    for (int c = 0; c < 9; c++)
                        ret.append(board[r][c]);
                    ret.append("\n");
                }
                return ret.toString();
            }
        }
    }
    

    【讨论】:

      【解决方案2】:

      我使用了一种没有回溯的方法,尽管可能是 while 循环。引用一本算法书,我读过“递归中的任何东西都不能使用迭代来复制”。

      我一直在用我的眼睛检查这一点,因为我无法将我的头包裹在递归方法上,即使递归是相对理解的:

      这个方法,我写了一些指导,在网格检查器中有一个错误,当我发现它时,它似乎现在可以工作了。我假设它是因为很难找到完整且有效的代码。 IOS SDK。

      #define WIDTH 9
      #define HEIGHT 9
      
      
      @interface ViewController ()
      //- (BOOL) numberConflicts:(int)testNum;
      - (BOOL) number:(int)n conflictsWithRow:(int)r;
      - (BOOL) number:(int)n conflictsWithColumn:(int)c;
      - (BOOL) number:(int)n conflictsWithSquareInPointX:(int)x andPointY:(int)y;
      - (BOOL) number:(int)n conflictsAtGridPointX:(int)xPoint andPointY:(int)yPoint;
      - (int) incrementSudokuValue:(int)v;
      @end
      
      
      static int sudoku[WIDTH][HEIGHT];
      
      @implementation ViewController
      
      - (void)viewDidLoad
      {
          [super viewDidLoad];
      
          /// Initialize it
          for (int x = 0; x < WIDTH; x++)
          {
              for (int y = 0; y < HEIGHT; y++)
              {
                  sudoku[x][y] = 0;
              }
          }
          ///
      
      
      
      
          int tries = 0;
          for (int j = 0; j < HEIGHT; j++)
          {
              for (int i = 0; i < WIDTH; i++)
              {
                  int num = arc4random()%9 + 1;
                  while ([self number:num conflictsAtGridPointX:i andPointY:j])
                  {
                      num = [self incrementSudokuValue:num];
                      tries++;
                      if (tries > 10) { //restart the column
                          tries = 0;
      
                          for(int count = 0; count < WIDTH; count++)
                          {
                              sudoku[count][j] = 0;
      
                          }
      
                          i = 0;
      
                      }
                  }
                  if(sudoku[i][j] == 0)
                     sudoku[i][j] = num; 
      
                  tries = 0;
                  for (int y = 0; y < HEIGHT; y++)
                  {
                      for (int x = 0; x < WIDTH; x++)
                      {
                          printf("%i ", sudoku[x][y]);
                      }
      
                      printf("\n");
                  }
      
                  printf("\n");
      
              }
          }
      
          for (int x = 0; x < WIDTH; x++)
          {
              for (int y = 0; y < HEIGHT; y++)
              {
                  printf("%i ", sudoku[y][x]);
              }
              printf("\n"); //newline
          }
      
          // Do any additional setup after loading the view, typically from a nib.
      }
      
      - (void)didReceiveMemoryWarning
      {
          [super didReceiveMemoryWarning];
          // Dispose of any resources that can be recreated.
      }
      
      
      
      - (BOOL) number:(int)n conflictsWithRow:(int)r;
      {
          for (int y = 0; y < HEIGHT; y++) {
              if (sudoku[y][r] == n) {
                  return YES;
              }
          }
      
          return NO;
      }
      
      - (BOOL) number:(int)n conflictsWithColumn:(int)c;
      {
          for (int x = 0; x < WIDTH; x++) {
              if (sudoku[c][x] == n) {
                  return YES;
              }
          }
      
          return NO;
      }
      
      - (BOOL) number:(int)n conflictsAtGridPointX:(int)xPoint andPointY:(int)yPoint;
      {
          if ([self number:n conflictsWithRow:yPoint])
          {
              return YES;
          }
      
          if ([self number:n conflictsWithColumn:xPoint])
          {
              return YES;
          }
      
          if ([self number:n conflictsWithSquareInPointX:xPoint andPointY:yPoint]) {
              return YES;
          }
      
      
          return NO;
      }
      
      - (BOOL) number:(int)n conflictsWithSquareInPointX:(int)x andPointY:(int)y;
      {
          int leftX = x - (x % 3); //used to use int division
          // leftX *= 3;
          int topY = y - (y % 3);
          // topY *= 3;
      
          int rightX = leftX + 2;
          int bottomY = topY + 2;
      
          for(int subY = topY; subY <= bottomY; subY++) //bug was here, used < instead of less N equal to...
          {
              for ( int subX = leftX; subX <= rightX; subX++)
              {
                  if (sudoku[subX][subY] == n) {
                      return YES;
                  }
              }
          }
      
          NSLog(@"Testing grid at %i, %i", x/3, y/3);
          NSLog(@"LeftX: %i TopY: %i", leftX, topY);
      
          return NO;
      }
      
      - (int) incrementSudokuValue:(int)v;
      {
          if (v < 9) {
              v++;
              return v;
          }
          return 1;
      }
      

      注意:头文件为空,如果需要,可以将其粘贴到 iOS 单视图应用程序中。

      注意:可能会无限循环(上面有时会这样做,但速度非常快),可能需要另一个更全局的“尝试”变量,并重新启动算法作为安全措施,或者给它一个种子/两者都做

      编辑:如果源网格是可解的(或不存在),则以下内容应避免无限循环


      #define WIDTH 9
      #define HEIGHT 9
      
      @interface ViewController ()
      //- (BOOL) numberConflicts:(int)testNum;
      - (BOOL) number:(int)n conflictsWithRow:(int)r;
      - (BOOL) number:(int)n conflictsWithColumn:(int)c;
      - (BOOL) number:(int)n conflictsWithSquareInPointX:(int)x andPointY:(int)y;
      - (BOOL) number:(int)n conflictsAtGridPointX:(int)xPoint andPointY:(int)yPoint;
      - (int) incrementSudokuValue:(int)v;
      @end
      
      static int sudoku[WIDTH][HEIGHT];
      
      @implementation ViewController
      
      - (BOOL) fillGridWithNext:(int)next;
      {
      
          for (int y = 0; y < HEIGHT; y++)
          {
              for (int x = 0; x < WIDTH; x++)
              {
                  if (sudoku[x][y] != 0)
                  {
                      if (x == 8 && y == 8) {
                          return YES;
                      }
                      continue;
                  }
      
                  for (int count = 0; count < (HEIGHT-1); count++)
                  {
                      if ([self number:next conflictsAtGridPointX:x andPointY:y])
                      {
                          next = [self incrementSudokuValue:next];
                      }
                      else
                      {
                          sudoku[x][y] = next;
                          if( [self fillGridWithNext:arc4random()%9+1])
                          {
                              return YES;
                          }
      
                      }
                  }
                  sudoku[x][y] = 0;
                  return NO;
              }
          }
      
          return NO;
      }
      
      - (void)viewDidLoad
      {
          [super viewDidLoad];
      
          /// Initialize it
          for (int x = 0; x < WIDTH; x++)
          {
              for (int y = 0; y < HEIGHT; y++)
              {
                  sudoku[x][y] = 0;
              }
          }
      
          sudoku[0][0]=9;
          int next;
          next = (arc4random()%9)+1;
      
          if( [self fillGridWithNext:next]) //seeded
          {
              NSLog(@"Solved");
          }
          else
          {
              NSLog(@"No solution");
          }
      
      
          for (int x = 0; x < WIDTH; x++)
          {
              for (int y = 0; y < HEIGHT; y++)
              {
                  printf("%i ", sudoku[y][x]);
              }
              printf("\n"); //newline
          }
          // Do any additional setup after loading the view, typically from a nib.
      }
      
      - (void)didReceiveMemoryWarning
      {
          [super didReceiveMemoryWarning];
          // Dispose of any resources that can be recreated.
      }
      
      
      
      - (BOOL) number:(int)n conflictsWithRow:(int)r;
      {
          for (int y = 0; y < HEIGHT; y++) {
              if (sudoku[y][r] == n) {
                  return YES;
              }
          }
      
          return NO;
      }
      
      - (BOOL) number:(int)n conflictsWithColumn:(int)c;
      {
          for (int x = 0; x < WIDTH; x++) {
              if (sudoku[c][x] == n) {
                  return YES;
              }
          }
      
          return NO;
      }
      
      - (BOOL) number:(int)n conflictsAtGridPointX:(int)xPoint andPointY:(int)yPoint;
      {
          if ([self number:n conflictsWithRow:yPoint])
          {
              return YES;
          }
      
          if ([self number:n conflictsWithColumn:xPoint])
          {
              return YES;
          }
      
          if ([self number:n conflictsWithSquareInPointX:xPoint andPointY:yPoint]) {
              return YES;
          }
      
      
          return NO;
      }
      
      - (BOOL) number:(int)n conflictsWithSquareInPointX:(int)x andPointY:(int)y;
      {
          int leftX = x - (x % 3); //used to use int division
          // leftX *= 3;
          int topY = y - (y % 3);
          // topY *= 3;
      
          int rightX = leftX + 2;
          int bottomY = topY + 2;
      
          for(int subY = topY; subY <= bottomY; subY++) //bug was here, used < instead of less N equal to...
          {
              for ( int subX = leftX; subX <= rightX; subX++)
              {
                  if (sudoku[subX][subY] == n) {
                      return YES;
                  }
              }
          }
      
          NSLog(@"Testing grid at %i, %i", x/3, y/3);
          NSLog(@"LeftX: %i TopY: %i", leftX, topY);
      
          return NO;
      }
      
      - (int) incrementSudokuValue:(int)v;
      {
          if (v < 9) {
              v++;
              return v;
          }
          return 1;
      }
      
      @end
      

      总结:第一个版本有缺陷,但(大部分)完成了工作。它随机生成每一行,如果该行无效,它会擦除​​并重新开始。这将消除源网格,并且可以永远存在,但大部分时间都有效。

      下面的代码使用递归。我认为它不能正确回溯,但它在我的测试中解决了空的和半种子的网格。我想我需要保存一个“状态”网格来回溯,但我没有这样做。我发布两者,因为他们都回答“蛮力”......我自己,我应该研究递归,我无法解释为什么较低的工作,我个人可以使用帮助。

      注意:第一个在完成时会在眨眼间完成……如果速度对您的应用程序而言不仅仅意味着可靠性(在这种情况下有点违反直觉,无限循环,呵呵)。

      【讨论】:

      • 为了防止算法被阻塞,当它位于任何 3 行的第 3 行时,我猜它应该使用“其他 2”网格的现有数字作为可用数字。这可以防止它“锁定”。如果这是正确的,那么这个算法就变成了一个完全随机的生成器。我认为求解器只是播种这个网格的问题。不过,它没有优化。这样的更改将使等式变得完美,消除所有可能的“锁定”以继续前进并需要使用 try++ 部分。不过,我无法证明这一点,抱歉,我还不是数学证明人
      【解决方案3】:

      这个简单的随机游走算法也应该可以工作(但效率低下 - 使用风险自负!!!):

      编辑: - 为无法解决的解决方案添加了修复程序。

      对于网格中的每个空单元格 数组 = Get_Legal_Numbers_for_cell(row,col); 如果(数组为空){ Clear_All_cells() } 别的 { number = Random_number_from(array); Put_Number_in_Cell(数字); }

      编辑 2

      如果有人有兴趣 here are described methods 使用基于随机的搜索解决数独问题。

      【讨论】:

      • 不,它不应该工作。单元格可能没有合法编号。考虑这个例子:123456789 456123___ 第二行空单元格的合法数字是多少?
      • 现在,它应该可以工作了,如果单元格没有合法数字,我们只需要重新启动算法。当然,这是非常低效的。是的 - 我知道其他有效的方法,比如回溯等等......
      • 这并不是一个真正简单的算法——对于初学者来说,它是随机的而不是蛮力的(因此可能永远找不到正确的解决方案)。此外,当您清除所有单元格并重新启动时,Get_Legal_Numbers_for_cell 如何计算出要更改的值?因为您填写的 n 个值中有 一个 可能不正确,但是您怎么知道要更改哪个?当你充实你的方法时,我想你会发现这远非简单。
      • @Dan Puzey Get_Legal_Numbers_for_cell 根本不计算要更改的值。它只是获取当前空单元格的所有合法数字。如果没有这样的数字 - 我们清除所有网格并重新启动算法。这种随机游走方法有什么问题?
      • 这些数字估计约为。 320 分之一的机会在每次尝试中找到解决方案。
      猜你喜欢
      • 2015-01-10
      • 2013-04-05
      • 1970-01-01
      • 2011-05-01
      • 2015-06-22
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多