免费注册 查看新帖 |

Chinaunix

  平台 论坛 博客 文库
最近访问板块 发新帖
查看: 3027 | 回复: 3
打印 上一主题 下一主题

解数独的代码 [复制链接]

论坛徽章:
0
跳转到指定楼层
1 [收藏(0)] [报告]
发表于 2007-05-28 18:46 |只看该作者 |倒序浏览
不知道大家玩过数独没,这是我做的一个解数独得代码.
新建一个文本文档,名字叫 Sudo.txt
里面的内容如下(下面这个数字矩阵):
0 3 0 0 0 7 0 0 4
6 0 2 0 4 1 0 0 0
0 5 0 0 3 0 9 6 7
0 4 0 0 0 3 0 0 6
0 8 7 0 0 0 3 5 0
9 0 0 7 0 0 0 2 0
7 1 8 0 2 0 0 4 0
0 0 0 1 6 0 8 0 9
4 0 0 5 0 0 0 3 0
保存后就可以了,这个矩阵也可以自己改, 0表示未知值.
如果这个数独不止一个解,所有的解都会被打印出来.
下面就是具体代码了,希望大家玩得愉快,有什么问题的话就留个言吧!


  1. #include <stdio.h>
  2. #define debug(x) puts(x)
  3. #define mask 0x1FF
  4. #define besure(x) (!(x&(x-1)))

  5. typedef struct sudo {
  6.     int sudo_p[9][9];
  7.     int sudo_set[3][9];
  8.     int sudo_x, sudo_y;
  9.     struct sudo *sudo_next;
  10. } t_sudo;

  11. void save(t_sudo **s, t_sudo *p)
  12. {
  13.     p->sudo_next = (*s);
  14.     *s = p;
  15. }

  16. t_sudo *load(t_sudo **s)
  17. {
  18.     t_sudo *p = *s;
  19.    
  20.     if (!s || !(*s))
  21.         return NULL;
  22.     (*s) = p->sudo_next;
  23.     return p;
  24. }

  25. int hex_to_int(int i)
  26. {
  27.     int j = 0;
  28.    
  29.     if (i == 0)
  30.        return 0;
  31.    
  32.     if (besure(i)) {
  33.         j = 1;
  34.         while (!(i & 0x1)) {
  35.               i >>= 1;
  36.               j++;
  37.         }
  38.     }
  39.     return j;
  40. }

  41. int Sudo_show (char *msg, t_sudo *s, FILE *fp)
  42. {
  43.     int i, j, count = 0;
  44.    
  45.     while (s) {
  46.         count ++;
  47.         if (msg) {
  48.            fputs(msg, fp);
  49.            fputs("\n", fp);
  50.         }
  51.         
  52.         for (i = 0; i < 9; ++i) {
  53.             for (j = 0; j < 9; ++j)
  54.                 fprintf(fp, "%d ", hex_to_int(s->sudo_p[i][j]));
  55.             fputs("\n", fp);
  56.         }
  57.         s = s->sudo_next;
  58.     }
  59.     return count;
  60. }

  61. int Sudo_init(char const* filename, t_sudo *s)
  62. {
  63.     FILE *fp;
  64.     int i, j, t;
  65.    
  66.     memset(s, 0, sizeof(t_sudo));
  67.     fp = fopen(filename, "r");
  68.     if (!fp) {
  69.         fprintf(stderr, "File open error!\n");
  70.         return -1;
  71.     }
  72.    
  73.     for (i = 0; i < 9; ++i) {
  74.         for (j = 0; j < 9; ++j) {
  75.             if (fscanf(fp, "%d", &t) != 1) {
  76.                 fprintf(stderr, "Scan error in line %d row %d\n", i, j);
  77.                 return -1;
  78.             }
  79.             if (t == 0)
  80.                 s->sudo_p[i][j] = mask;
  81.             else
  82.                 s->sudo_p[i][j] = 1 << (t-1);
  83.         }
  84.     }
  85.    
  86.     fclose(fp);
  87.     Sudo_show("Sudo Init Complete!", s, stdout);
  88.     return 0;
  89. }

  90. int sudo_check(t_sudo *s, int *x, int *y)
  91. {
  92.     int i, j, k, tmp;
  93.    
  94.     for (i = 0, tmp = 0; i < 9; ++i) {
  95.         for (j = 0; j < 9; ++j) {
  96.             if (!besure(s->sudo_p[i][j])) {
  97.                 *x = i;
  98.                 *y = j;
  99.                 return 1;                                
  100.             }
  101.             tmp |= s->sudo_p[i][j];
  102.         }
  103.         
  104.         if (tmp != mask)
  105.             return 2;
  106.     }
  107.    
  108.     for (k = 0; k < 9; ++k) {
  109.         for (i = (k/3)*3, tmp = 0; i < (k/3)*3+3; ++i)
  110.             for (j = (k%3)*3; j < (k%3)*3+3; ++j)
  111.                 tmp |= s->sudo_p[i][j];
  112.             
  113.         if (tmp != mask)
  114.             return 2;
  115.     }
  116.         
  117.     return 0;
  118. }

  119. #define zerolowbit(x) (x & (x-1))
  120. #define lowbit(x) ((x & (x-1)) ^ x)

  121. #define CLOSE(s, i, j) if (besure(s->sudo_p[i][j])) { \
  122.                             s->sudo_set[0][i] &= ~s->sudo_p[i][j];    \
  123.                             s->sudo_set[1][j] &= ~s->sudo_p[i][j];    \
  124.                             s->sudo_set[2][(i/3)*3+j/3] &= ~s->sudo_p[i][j]; \
  125.                         }

  126. #define CLOSESTACK(s, i, j)  if (!besure(s->sudo_p[i][j])) { \
  127.                                 s->sudo_p[i][j] &= s->sudo_set[0][i]; \
  128.                                 s->sudo_p[i][j] &= s->sudo_set[1][j]; \
  129.                                 s->sudo_p[i][j] &= s->sudo_set[2][(i/3)*3+j/3]; \
  130.                                 if (s->sudo_p[i][j] == 0) {\
  131.                                     sp = 0; \
  132.                                     goto load; \
  133.                                 } \
  134.                                 if (besure(s->sudo_p[i][j])) { \
  135.                                     s->sudo_set[0][i] &= ~s->sudo_p[i][j];    \
  136.                                     s->sudo_set[1][j] &= ~s->sudo_p[i][j];    \
  137.                                     s->sudo_set[2][(i/3)*3+j/3] &= ~s->sudo_p[i][j]; \
  138.                                     stack[sp++] = i; \
  139.                                     stack[sp++] = j; \
  140.                                 } \
  141.                             }

  142. int Sudo(t_sudo *s)
  143. {
  144.     int i, j, x, y, ret, k = 0;
  145.     int stack[182];
  146.     int sp = 0;
  147.     t_sudo *t, *head = NULL, *res=NULL;
  148.    
  149.     t = malloc(sizeof(t_sudo));
  150.     *t = *s;
  151.     s = t;
  152.         
  153.     for (i = 0; i < 3; ++i)
  154.         for (j = 0; j < 9; ++j)
  155.             s->sudo_set[i][j] = mask;
  156.    
  157.     for (i = 0; i < 9; ++i) {
  158.         for (j = 0; j < 9; ++j) {
  159.             CLOSE(s, i, j)
  160.         }
  161.     }
  162.    
  163.     for (i = 0; i < 9; ++i) {
  164.         for (j = 0; j < 9; ++j) {
  165.             CLOSESTACK(s, i, j);
  166.         }
  167.     }
  168.    
  169.     while (1) {
  170.         while (sp != 0) {
  171.               y = stack[--sp];
  172.               x = stack[--sp];
  173.               for (i = 0; i < 9; ++i) {
  174.                   CLOSESTACK(s, i, y);
  175.                   CLOSESTACK(s, x, i);
  176.               }
  177.               for (i = (x/3)*3; i < (x/3)*3+3; ++i)
  178.                   for (j = (y/3)*3; j < (y/3)*3+3; ++j)
  179.                       CLOSESTACK(s, i, j);
  180.         }
  181.         
  182.         ret = sudo_check(s, &i, &j);
  183.         if (ret == 0) {
  184.            s->sudo_next = res;
  185.            res = s;
  186.            goto notfreeload;
  187.         } else if (ret == 2) {
  188.            goto load;
  189.         }
  190.         t = malloc(sizeof(t_sudo));
  191.         *t = *s;
  192.         t->sudo_x = i;
  193.         t->sudo_y = j;
  194.         t->sudo_p[i][j] = zerolowbit(t->sudo_p[i][j]) ;      
  195.         save(&head, t);
  196.         s->sudo_p[i][j] = lowbit(s->sudo_p[i][j]) ;
  197.         CLOSE(s, i, j);
  198.         stack[sp++] = i;
  199.         stack[sp++] = j;
  200.         continue;
  201.         load:
  202.             free(s);
  203.         notfreeload:
  204.             s = load(&head);
  205.             if (!s) {
  206.                 break;
  207.             }
  208.             CLOSE(s, s->sudo_x, s->sudo_y);
  209.     }
  210.    
  211.     k = Sudo_show("-----------------", res, stdout);
  212.     printf("-----------------\n%d result(s) Found!\n", k);
  213.    
  214.     while(res) {
  215.         s = res->sudo_next;
  216.         free(res);
  217.         res = s;
  218.     }
  219. }

  220. int main()
  221. {
  222.     t_sudo p;
  223.    
  224.     Sudo_init("Sudo.txt" , &p);
  225.     Sudo(&p);
  226.    
  227.     system("pause");
  228.     return 0;
  229. }

复制代码

[ 本帖最后由 xxandxx 于 2007-5-29 09:56 编辑 ]

论坛徽章:
0
2 [报告]
发表于 2007-05-28 19:30 |只看该作者
Amazing! 算法实现很精简,代码风格也很赞,这个要顶。

论坛徽章:
0
3 [报告]
发表于 2007-05-28 19:45 |只看该作者
刚玩了一会儿,好像存在一些bug,当数独难度比较大的时候容易出现问题,例如下面的一个数独:
1 0 0 0 0 2 3 0 0
4 0 0 0 5 3 0 0 0
0 0 0 6 7 8 0 0 0
0 7 0 0 9 0 0 5 0
0 8 0 0 0 0 0 4 0
0 5 0 0 3 0 0 2 0
0 0 0 7 2 1 0 0 0
0 0 0 5 8 0 0 0 6
0 0 9 3 0 0 0 0 7

它存在唯一解:
1 6 8 9 4 2 3 7 5
4 2 7 1 5 3 9 6 8
3 9 5 6 7 8 4 1 2
2 7 1 4 9 6 8 5 3
6 8 3 2 1 5 7 4 9
9 5 4 8 3 7 6 2 1
8 3 6 7 2 1 5 9 4
7 4 2 5 8 9 1 3 6
5 1 9 3 6 4 2 8 7

这个程序找到了此解,但是还找出了另外1254个错误的解,例如
1 6 8 9 4 2 3 8 8
4 9 8 1 5 3 9 9 9
9 9 5 6 7 8 9 9 9
3 7 4 8 9 6 8 5 8
9 8 6 2 1 5 9 4 9
9 5 6 8 3 7 9 2 9
8 4 8 7 2 1 9 9 9
7 4 7 5 8 9 4 3 6
8 2 9 3 6 4 8 8 7

论坛徽章:
0
4 [报告]
发表于 2007-05-29 10:00 |只看该作者
谢谢 emacsnw指出这个问题
我在sudo_check中作的弱检测,因此一些不正确的解也被当作了正确解。
现在把sudo_check中的检测作了强化,可以正确解出 emacsnw那个比较难的数独了。
您需要登录后才可以回帖 登录 | 注册

本版积分规则 发表回复

  

北京盛拓优讯信息技术有限公司. 版权所有 京ICP备16024965号-6 北京市公安局海淀分局网监中心备案编号:11010802020122 niuxiaotong@pcpop.com 17352615567
未成年举报专区
中国互联网协会会员  联系我们:huangweiwei@itpub.net
感谢所有关心和支持过ChinaUnix的朋友们 转载本站内容请注明原作者名及出处

清除 Cookies - ChinaUnix - Archiver - WAP - TOP