【问题标题】:Generating Sudoku Puzzle生成数独谜题
【发布时间】:2019-12-06 14:40:44
【问题描述】:

我正在尝试编写一段代码,为我生成一个有效的数独游戏。

我的算法:

  1. 用 0 初始化所有字段
  2. 遵守规则,将 1-9 的 20 个随机值设置为随机字段
  3. 使用回溯算法解决难题

我的问题:

  1. 有时它会在 1 秒内生成一个有效的数独游戏。
  2. 有时它无法生成有效的数独并且我得到一个错误,这没关系,因为我可以返回算法中的第 1 步。
  3. 有时它无法生成有效的数独,我得到一个错误,但它需要大约 2-3 分钟,这是不行的。

如何解决我的问题? 特别是问题3。 我可以只计算秒数,如果超过 5 秒就返回算法的第 1 步吗?

或者有人有更好的主意吗?

提前致谢。

这是我的代码:

#include <stdio.h>
#include <stdlib.h>

#define N 9
#define UNASSIGNED 0


typedef enum {false, true} bool;
typedef struct { 
    char number;
    bool editable; 
} GRID;

void print_sudoku(GRID **g){
    char row=0, col=0;
    for(row=0; row<N; row++){
        for(col=0; col<N; col++){
            printf("%d ", g[row][col].number);
        }
        printf("\n");
    }
}


GRID ** create_sudoku_grid(){
    char i, row, col;
    GRID **g = (GRID **) malloc(N * sizeof(GRID *));
    for (i=0; i<N; i++) {
        g[i] = (GRID *) malloc(N * sizeof(GRID)); 
    }

    for(row=0; row<N; row++){
        for(col=0; col<N; col++){
            g[row][col].number = UNASSIGNED;
            g[row][col].editable = true;
        }
    }
    return g;
}



bool find_unassigned_field(GRID **g, int *row, int *col){
    for (*row = 0; *row < N; (*row)++) {
        for (*col = 0; *col < N; (*col)++) { 
            if (g[*row][*col].number == UNASSIGNED){
                return true;    
            } 
        }
    }
    return false; 
}

bool validate_row(GRID **g, int row, int num) { 
    for (int col = 0; col < N; col++) {
        if (g[row][col].number == num) {
            return false; 
        }
    }
    return true; 
} 

bool validate_col(GRID **g, int col, int num) { 
    for (int row = 0; row < N; row++) {
        if (g[row][col].number == num) {
            return false; 
        }
    }
    return true; 
} 


bool validate_box(GRID **g, int row, int col, int num) { 
    for (int r = 0; r < 3; r++) {
        for (int c = 0; c < 3; c++) { 
            if (g[r+row][c+col].number == num) {
                return false; 
            }
        }
    }
    return true; 
} 


bool validate_field(GRID **g, int row, int col, int num){
    bool valrow, valcol, valbox, valunassigned;

    valrow = validate_row(g, row, num); 
    valcol = validate_col(g, col, num);
    valbox = validate_box(g, row - row%3 , col - col%3, num);
    valunassigned = g[row][col].number==UNASSIGNED; 
    return (valrow && valcol && valbox && valunassigned);

}


bool generate_sudoku(GRID **g) { 
    int row, col; 

    // If there is no unassigned location, we are done 
    if (!find_unassigned_field(g, &row, &col)) {
        return true; // success! 
    }

    // consider digits 1 to 9 
    for (int num = 1; num <= 9; num++) { 
        // if looks promising 
        if (validate_field(g, row, col, num)) { 
            // make tentative assignment 
            g[row][col].number = num; 

            // return, if success, yay! 
            if (generate_sudoku(g)) {
                return true; 
            }
            // failure, unmake & try again 
            g[row][col].number = UNASSIGNED; 
        } 
    } 
    return false; // this triggers backtracking 
} 

void random_init_grid(GRID **g){
    int row, col, num;
    srand(time(0));

    for(int cntr=0; cntr<20;){
        row = rand() % N;
        col = rand() % N;
        num = rand() % N + 1;
        if(g[row][col].number == UNASSIGNED){
            if(validate_field(g, row, col, num)){
                g[row][col].number = num;
                cntr++;
            }
        }

    }
}

int main(int argc, char *argv[]) {
    GRID **g = create_sudoku_grid();
    random_init_grid(g);

    if(generate_sudoku(g)){
        printf("OK\n\n");
    } else {
        printf("\nNOT OK\n\n");
    }
    print_sudoku(g);
}

【问题讨论】:

  • 你可以这样做,是的。
  • 您的算法是否检查是否存在只有一个可能的解决方案?
  • 你有没有调查过你的算法的哪一部分是慢的?回溯搜索是否有时需要很长时间,或者是因为您生成了大量网格而被拒绝,因为它们没有解决方案(或多个解决方案)?
  • 欢迎来到 StackOverflow。见minimal, reproducible example。在您发布 MRE 代码并准确说明问题之前,我们无法有效地帮助您。我们应该能够将您发布的代码粘贴到文本文件中并重现您指定的问题。我不明白#1 是怎么回事;你说#2没问题。您确实不为#3 给出了可重现的问题。代替随机化,硬编码一个给你这个问题的起始网格。跟踪代码中的慢点。 那么你的帖子很好。
  • OT:发布的代码动态分配数组数组来存储大小为 N 的网格,这是一个在编译时已知的常数。您可以将该网格声明为 GRID g[N][N] 并传递一个指针 GRID g[][N]。

标签: c algorithm backtracking sudoku


【解决方案1】:

Sudoko 是一个计算困难的问题,使用蛮力和无知可能达到 10^100 的数量级。它可能需要比 2-3 分钟更长的时间!有时,因为数字布局,这比那更容易。

在一种方法中,您可以计算迭代次数,如果超过了就放弃。您的关键位是您递归调用 generate_soduko() 的块 - 如果您在这里结束太多次,您就会遇到麻烦。

为此,我更改了您的程序,在其执行时设置了一个 1s 警报,计算该块中的次数;如果警报到期,打印计数器并退出;如果最后没有打印计数器以供参考。在我的机器上,1s == 500,000 次迭代。

*** sud.c~  2019-12-06 14:30:21.000000000 -0500
--- sud.c   2019-12-06 14:30:57.000000000 -0500
***************
*** 1,10 ****
  #include <stdio.h>
  #include <stdlib.h>

  #define N 9
  #define UNASSIGNED 0

- 
  typedef enum {false, true} bool;
  typedef struct { 
      char number;
--- 1,21 ----
  #include <stdio.h>
  #include <stdlib.h>
+ #include <signal.h>
+ #include <unistd.h>
+ #include <time.h>
+ 
+ volatile long  counter;
+ void alrm(int signo) {
+   char buf[64];
+   int n;
+   n = sprintf(buf, "failed after %ld iter\n", counter);
+   write(2, buf, n);
+   _exit(1);
+ }

  #define N 9
  #define UNASSIGNED 0

  typedef enum {false, true} bool;
  typedef struct { 
      char number;
***************
*** 106,111 ****
--- 117,123 ----
      for (int num = 1; num <= 9; num++) { 
          // if looks promising 
          if (validate_field(g, row, col, num)) { 
+       counter++;
              // make tentative assignment 
              g[row][col].number = num; 

***************
*** 139,145 ****
  }

  int main(int argc, char *argv[]) {
!     GRID **g = create_sudoku_grid();
      random_init_grid(g);

      if(generate_sudoku(g)){
--- 151,161 ----
  }

  int main(int argc, char *argv[]) {
! 
!     GRID **g;
!   signal(SIGALRM, alrm);
!   alarm(1);
!     g = create_sudoku_grid();
      random_init_grid(g);

      if(generate_sudoku(g)){
***************
*** 148,151 ****
--- 164,168 ----
          printf("\nNOT OK\n\n");
      }
      print_sudoku(g);
+     printf("iter = %ld\n", counter);
  }

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2017-05-18
    • 2021-04-09
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-10-28
    • 2010-09-08
    • 1970-01-01
    相关资源
    最近更新 更多