楼主: newkid

用PLSQL解数独(SUDOKU)

[复制链接]
论坛徽章:
520
奥运会纪念徽章:垒球
日期:2008-09-15 01:28:12生肖徽章2007版:鸡
日期:2008-11-17 23:40:58生肖徽章2007版:马
日期:2008-11-18 05:09:48数据库板块每日发贴之星
日期:2008-11-29 01:01:02数据库板块每日发贴之星
日期:2008-12-05 01:01:03生肖徽章2007版:虎
日期:2008-12-10 07:47:462009新春纪念徽章
日期:2009-01-04 14:52:28数据库板块每日发贴之星
日期:2009-02-08 01:01:03生肖徽章2007版:蛇
日期:2009-03-09 22:18:532009日食纪念
日期:2009-07-22 09:30:00
51#
 楼主| 发表于 2010-3-20 02:06 | 只看该作者
多谢野花解惑。你举的三个格子{1,2,4}, {1,2}, {1,2},前提是要处于同一行、或同一列、或同一区对吗?
这个思路是可以理解的,但实现起来好像挺啰唆的,得嵌套好几个循环?

SQL只在穷举方面有优势,中间需要裁剪的话就很难处理了,到10G为止只有CONNECT BY可以做到但它又有很多限制。

使用道具 举报

回复
论坛徽章:
484
ITPUB北京香山2007年会纪念徽章
日期:2007-01-24 14:35:02ITPUB北京九华山庄2008年会纪念徽章
日期:2008-01-21 16:50:24ITPUB北京2009年会纪念徽章
日期:2009-02-09 11:42:452010新春纪念徽章
日期:2010-03-01 11:04:552010数据库技术大会纪念徽章
日期:2010-05-13 10:04:272010系统架构师大会纪念
日期:2010-09-04 13:35:54ITPUB9周年纪念徽章
日期:2010-10-08 09:28:512011新春纪念徽章
日期:2011-02-18 11:43:32ITPUB十周年纪念徽章
日期:2011-11-01 16:19:412012新春纪念徽章
日期:2012-01-04 11:49:54
52#
发表于 2010-3-20 02:13 | 只看该作者
嗯,例子中的这三个格子都是在同一行
假设我们当前按该格子所在行来执行4,在该行其他未确定值的格子都执行了按行、列和方阵的消去后
这时,就可能出现下面的情况,按最简单的,还有三个格子未确定情况,他们的pList分别是
{1,2,4}, {1,2}, {1,2}
后两个格子pList中的4,是在按列查找并消去的环节中消除的


要是有个实例就好了,我拿程序跑一遍,然后看着一轮一轮的输出结果就更直观了
顺便补充一下上面的说法,三个格子未必是连续的,就算是连续的,也有可能不在一个方阵内
所以,后两个格子pList中的4,也可能是在按方阵查找并消去的环节中消除的

使用道具 举报

回复
论坛徽章:
484
ITPUB北京香山2007年会纪念徽章
日期:2007-01-24 14:35:02ITPUB北京九华山庄2008年会纪念徽章
日期:2008-01-21 16:50:24ITPUB北京2009年会纪念徽章
日期:2009-02-09 11:42:452010新春纪念徽章
日期:2010-03-01 11:04:552010数据库技术大会纪念徽章
日期:2010-05-13 10:04:272010系统架构师大会纪念
日期:2010-09-04 13:35:54ITPUB9周年纪念徽章
日期:2010-10-08 09:28:512011新春纪念徽章
日期:2011-02-18 11:43:32ITPUB十周年纪念徽章
日期:2011-11-01 16:19:412012新春纪念徽章
日期:2012-01-04 11:49:54
53#
发表于 2010-3-20 02:21 | 只看该作者
这个思路是可以理解的,但实现起来好像挺啰唆的,得嵌套好几个循环?

____________________________________________________________
两个循环吧

1层循环体begin:
    遍历所有包含未确定数字的格子(pList长度大于1的)
2层循环体begin:
    针对每个格子分别按行、列、方阵消去不可能的数字
2层循环体end;
    检查当前方阵与进入本次2层循环前的方阵是否有差别,若有,继续1层循环,若无,退出1层循环
1层循环体end;


要加上尝试环节就是三个循环了

使用道具 举报

回复
论坛徽章:
520
奥运会纪念徽章:垒球
日期:2008-09-15 01:28:12生肖徽章2007版:鸡
日期:2008-11-17 23:40:58生肖徽章2007版:马
日期:2008-11-18 05:09:48数据库板块每日发贴之星
日期:2008-11-29 01:01:02数据库板块每日发贴之星
日期:2008-12-05 01:01:03生肖徽章2007版:虎
日期:2008-12-10 07:47:462009新春纪念徽章
日期:2009-01-04 14:52:28数据库板块每日发贴之星
日期:2009-02-08 01:01:03生肖徽章2007版:蛇
日期:2009-03-09 22:18:532009日食纪念
日期:2009-07-22 09:30:00
54#
 楼主| 发表于 2010-3-20 03:14 | 只看该作者
你也隐藏太多细节了吧?我说的单单是你的步骤4,即从{1,2,4}, {1,2}, {1,2}中间确定第一个格子为4这个实现:

LOOP1 遍历所有plist 长度大于1的格子    -------- 即{1,2,4}, {1,2}, {1,2}及其他
   LOOP2 遍历LOOP1.当前格子所有plist 值 ------第一个{1,2,4}
      LOOP3 遍历和LOOP1.当前格子同一行或同一列或同一区并且plist 长度大于1的所有plist值 ---- 即后两个格子{1,2}, {1,2}
          如果该值=LOOP2.PLIST.当前值则退出LOOP3    ----- 前面的1,2都会中间退出,4不会
      END LOOP3
      如果LOOP2.PLIST.当前值从未出现,则确定 LOOP1.当前值, 退出LOOP2 ----- 一直到4才确定
   END LOOP2
END LOOP1

大体应该是这样吧?

[ 本帖最后由 newkid 于 2010-3-20 03:18 编辑 ]

使用道具 举报

回复
论坛徽章:
484
ITPUB北京香山2007年会纪念徽章
日期:2007-01-24 14:35:02ITPUB北京九华山庄2008年会纪念徽章
日期:2008-01-21 16:50:24ITPUB北京2009年会纪念徽章
日期:2009-02-09 11:42:452010新春纪念徽章
日期:2010-03-01 11:04:552010数据库技术大会纪念徽章
日期:2010-05-13 10:04:272010系统架构师大会纪念
日期:2010-09-04 13:35:54ITPUB9周年纪念徽章
日期:2010-10-08 09:28:512011新春纪念徽章
日期:2011-02-18 11:43:32ITPUB十周年纪念徽章
日期:2011-11-01 16:19:412012新春纪念徽章
日期:2012-01-04 11:49:54
55#
发表于 2010-3-20 12:43 | 只看该作者

回复 #54 newkid 的帖子

a........
我告诉你的是总体解题思路,哈哈
你问的是局部的呀~~

首先明确行、列、方阵,其实都是等价的模型
前提已经明确了该三个格子在一行上,所以呢
嗯,某个细节貌似确实隐藏了(实际上没法说那么细,哈哈,当时大半夜的),就是该行所有未确定的数字也会放在一个集合uList中(uncertainList)
然后呢,针对每个未确定的数字都有个配对的集合,来记录该数字在该行的哪些格子中出现,集合的长度,也就是出现的次数
于是对该行的这些格子做遍历,最后获得1出现了3次,2出现了3次,4出现了1次而且是出现在第一个格子上

实际上,这时候,只需要对行遍历即可,不用对列和方阵再去遍历


你的算法我大致看了下,也和我的一个意思
但是,你的时间复杂度是O(n,2),偶的是O(n)
你的是用时间来换取只使用少量的存储空间,呵呵

使用道具 举报

回复
论坛徽章:
520
奥运会纪念徽章:垒球
日期:2008-09-15 01:28:12生肖徽章2007版:鸡
日期:2008-11-17 23:40:58生肖徽章2007版:马
日期:2008-11-18 05:09:48数据库板块每日发贴之星
日期:2008-11-29 01:01:02数据库板块每日发贴之星
日期:2008-12-05 01:01:03生肖徽章2007版:虎
日期:2008-12-10 07:47:462009新春纪念徽章
日期:2009-01-04 14:52:28数据库板块每日发贴之星
日期:2009-02-08 01:01:03生肖徽章2007版:蛇
日期:2009-03-09 22:18:532009日食纪念
日期:2009-07-22 09:30:00
56#
 楼主| 发表于 2010-3-21 00:01 | 只看该作者
你这个结构很好,改天我有空得来试一下。

使用道具 举报

回复
论坛徽章:
0
57#
发表于 2010-3-21 22:10 | 只看该作者
顶!谢谢

使用道具 举报

回复
论坛徽章:
0
58#
发表于 2010-3-24 19:48 | 只看该作者
强!!!!!!!!!!!!!!1

使用道具 举报

回复
论坛徽章:
520
奥运会纪念徽章:垒球
日期:2008-09-15 01:28:12生肖徽章2007版:鸡
日期:2008-11-17 23:40:58生肖徽章2007版:马
日期:2008-11-18 05:09:48数据库板块每日发贴之星
日期:2008-11-29 01:01:02数据库板块每日发贴之星
日期:2008-12-05 01:01:03生肖徽章2007版:虎
日期:2008-12-10 07:47:462009新春纪念徽章
日期:2009-01-04 14:52:28数据库板块每日发贴之星
日期:2009-02-08 01:01:03生肖徽章2007版:蛇
日期:2009-03-09 22:18:532009日食纪念
日期:2009-07-22 09:30:00
59#
 楼主| 发表于 2010-4-2 01:47 | 只看该作者

参考lastwinner思路写了一个版本。
我以前的数据结构是“某个空格有哪些可能值”,现在则是“某个值可能出现在哪些位置”,这是为了方便实现lastwinner 的第四步推理。
当所有消除法都运行完毕,仍然可能有空格,这时候就得用回溯法了,这一步还没有实现。
比如下面这个题目就求不出解:
   s(1):= '1    7 9 ' ;
   s(2):= ' 3  2   8' ;
   s(3):= '  96  5  ' ;
   s(4):= '  53  9  ' ;
   s(5):= ' 1  8   2' ;
   s(6):= '6    4   ' ;
   s(7):= '3      1 ' ;
   s(8):= ' 4      7' ;
   s(9):= '  7   3  ' ;

lastwinner说这个方法求不出多个解的题目,但上述题目明明只有唯一解,我用其他方法验证过了。如果野花兄的程序能解出来请赐教。

CREATE OR REPLACE PROCEDURE sudoku_lw(p_str IN VARCHAR2 DEFAULT NULL)
AS
   TYPE t_num IS TABLE OF NUMBER;    ---- 三维数组
   TYPE t2_num IS TABLE OF t_num;
   TYPE t3_num IS TABLE OF t2_num;
   
   TYPE t_str IS TABLE OF VARCHAR2(10) INDEX BY BINARY_INTEGER;

   s t_str;      ---- 字符串数组,存放题目

   v_continue NUMBER;
   
   e t_num := t_num(1,2,3,4,5,6,7,8,9);      ---- 初始化数组
   e2 t2_num := t2_num(e,e,e,e,e,e,e,e,e);   ---- 初始化数组, 第二维
   r t3_num := t3_num(e2,e2,e2,e2,e2,e2,e2,e2,e2); ----- 如果r(i)(j)(k)有数据,则表示数字i可能出现在第j行第k列
   c t3_num := r;   ----- 如果c(i)(j)(k)有数据,则表示数字i可能出现在第k行第j列
   a t3_num := r;   ----- 如果a(i)(j)(k)有数据,则表示数字i可能出现在第k区第k列个位置
                    ----- 从左到右、从上到下编为9个区,每个区里面又按同样顺序编1-9号
   
   get_a t2_num := e2;       ------ 存放行列坐标和区号的换算,第i行第j列对应的区号是 get_a(i)(j)
   get_a_num t2_num := e2;   ------ 存放行列坐标和区内位置号的换算,第i行第j列对应的区内位置编号是 get_a_num(i)(j)
   
   PROCEDURE known (        ----- 已知值p_val出现在p_row行和p_col列,把它从行、列、区的候选值清单中删除。
             p_row IN NUMBER
            ,p_col IN NUMBER
            ,p_val IN NUMBER
             )
   AS
   BEGIN
      r(p_val)(p_row).DELETE;   ----- 本行已经不可能出现p_val了
      c(p_val)(p_col).DELETE;   ----- 本列已经不可能出现p_val了
      a(p_val)(get_a(p_row)(p_col)).DELETE;  ----- 本区已经不可能出现p_val了  
      
      FOR i IN 1..9 LOOP
          r(i)(p_row).DELETE(p_col);   ----- 所有其他候选值都不会出现在p_row,p_col 这个坐标了
          c(i)(p_col).DELETE(p_row);
          a(i)(get_a(p_row)(p_col)).DELETE(get_a_num(p_row)(p_col));

          r(p_val)(i).DELETE(p_col);   ------ p_val不会出现在同一列的其他行了
          c(p_val)(i).DELETE(p_row);   ------ p_val不会出现在同一行的其他列了

          FOR j IN 1..9 LOOP  ------ 所有区中只要行,列和本坐标有交叉,也就不会出现p_val了
              a(p_val)(get_a(p_row)(j)).DELETE(get_a_num(p_row)(j));
              a(p_val)(get_a(j)(p_col)).DELETE(get_a_num(j)(p_col));
          END LOOP;

      END LOOP;
      
      s(p_row) := SUBSTR(s(p_row),1,p_col-1)||p_val||SUBSTR(s(p_row),p_col+1); ---- 把已知值拼到方阵中
   END known;

BEGIN

   --- 测试用例
   s(1):= '3 58  176';
   s(2):= '1   5   2';
   s(3):= '      549';
   s(4):= ' 52 986  ';
   s(5):= '43  12  7';
   s(6):= '   5  4  ';
   s(7):= '5   6 894';
   s(8):= ' 69 7   5';
   s(9):= '   9   61';

   FOR i IN 1..9 LOOP
       IF p_str IS NOT NULL THEN
          s(i):= SUBSTR(p_str,(i-1)*9+1,9);
       END IF;
      
       FOR j IN 1..9 LOOP
           get_a(i)(j) := (CEIL(i/3)-1)*3+CEIL(j/3);      ----- 预先算好行列坐标和区以及区内编号的对应关系并保存到数组中
           get_a_num(i)(j) :=  (MOD(i-1,3)+1 -1 )*3 + MOD(j-1,3)+1;
           
           IF SUBSTR(s(i),j,1) BETWEEN '1' AND '9' THEN
              known(i,j,SUBSTR(s(i),j,1));
           END IF;
           
       END LOOP;
              
   END LOOP;
   
   
   v_continue := 1;
   WHILE v_continue=1 LOOP
         v_continue := 0;   
         FOR i IN 1..9 LOOP
             FOR j IN 1..9 LOOP
                 IF r(i)(j).COUNT=1 THEN     ------- lastwinner 的第四步推理
                    v_continue := 1;
                    known(j,r(i)(j)(r(i)(j).FIRST),i);  
                 END IF;
                 IF c(i)(j).COUNT=1 THEN        
                    v_continue := 1;
                    known(c(i)(j)(c(i)(j).FIRST),j,i);  
                 END IF;

                 IF a(i)(j).COUNT=1 THEN        
                    v_continue := 1;
                    known(get_a(j)(a(i)(j).FIRST),get_a_num(j)(a(i)(j).FIRST),i);  
                 END IF;

             END LOOP;
         END LOOP;
   END LOOP;
   
   DBMS_OUTPUT.PUT_LINE('result:');
   FOR i IN 1..9 LOOP  ------ 输出答案
       DBMS_OUTPUT.PUT_LINE('~'||s(i));
   END LOOP;

END sudoku_lw;
/

使用道具 举报

回复
论坛徽章:
484
ITPUB北京香山2007年会纪念徽章
日期:2007-01-24 14:35:02ITPUB北京九华山庄2008年会纪念徽章
日期:2008-01-21 16:50:24ITPUB北京2009年会纪念徽章
日期:2009-02-09 11:42:452010新春纪念徽章
日期:2010-03-01 11:04:552010数据库技术大会纪念徽章
日期:2010-05-13 10:04:272010系统架构师大会纪念
日期:2010-09-04 13:35:54ITPUB9周年纪念徽章
日期:2010-10-08 09:28:512011新春纪念徽章
日期:2011-02-18 11:43:32ITPUB十周年纪念徽章
日期:2011-11-01 16:19:412012新春纪念徽章
日期:2012-01-04 11:49:54
60#
发表于 2010-4-3 17:46 | 只看该作者

回复 #59 newkid 的帖子

原始数据:
100007090
030020008
009600500
005300900
010080002
600004000
300000010
040000007
007000300



最后结果:
>>>>>>>>>>>Data from Vector<<<<<<<<<<
1 0 0 0 0 7 0 9 0
0 3 0 0 2 0 0 0 8
0 0 9 6 0 0 5 0 0
0 0 5 3 0 0 9 0 0
0 1 0 0 8 0 0 0 2
6 0 0 0 0 4 0 0 0
3 0 0 0 0 0 0 1 0
0 4 1 0 0 0 0 0 7
0 0 7 0 0 0 3 0 0

中间过程:
第 1 轮循环,方阵

>>>>>>>>>>>Data from Vector<<<<<<<<<<
1 0 0 0 0 7 0 9 0
0 3 0 0 2 0 0 0 8
0 0 9 6 0 0 5 0 0
0 0 5 3 0 0 9 0 0
0 1 0 0 8 0 0 0 2
6 0 0 0 0 4 0 0 0
3 0 0 0 0 0 0 1 0
0 4 1 0 0 0 0 0 7
0 0 7 0 0 0 3 0 0
第 1 轮循环,行

>>>>>>>>>>>Data from Vector<<<<<<<<<<
1 0 0 0 0 7 0 9 0
0 3 0 0 2 0 0 0 8
0 0 9 6 0 0 5 0 0
0 0 5 3 0 0 9 0 0
0 1 0 0 8 0 0 0 2
6 0 0 0 0 4 0 0 0
3 0 0 0 0 0 0 1 0
0 4 1 0 0 0 0 0 7
0 0 7 0 0 0 3 0 0
第 1 轮循环,列

>>>>>>>>>>>Data from Vector<<<<<<<<<<
1 0 0 0 0 7 0 9 0
0 3 0 0 2 0 0 0 8
0 0 9 6 0 0 5 0 0
0 0 5 3 0 0 9 0 0
0 1 0 0 8 0 0 0 2
6 0 0 0 0 4 0 0 0
3 0 0 0 0 0 0 1 0
0 4 1 0 0 0 0 0 7
0 0 7 0 0 0 3 0 0
第 2 轮循环,方阵

>>>>>>>>>>>Data from Vector<<<<<<<<<<
1 0 0 0 0 7 0 9 0
0 3 0 0 2 0 0 0 8
0 0 9 6 0 0 5 0 0
0 0 5 3 0 0 9 0 0
0 1 0 0 8 0 0 0 2
6 0 0 0 0 4 0 0 0
3 0 0 0 0 0 0 1 0
0 4 1 0 0 0 0 0 7
0 0 7 0 0 0 3 0 0
第 2 轮循环,行

>>>>>>>>>>>Data from Vector<<<<<<<<<<
1 0 0 0 0 7 0 9 0
0 3 0 0 2 0 0 0 8
0 0 9 6 0 0 5 0 0
0 0 5 3 0 0 9 0 0
0 1 0 0 8 0 0 0 2
6 0 0 0 0 4 0 0 0
3 0 0 0 0 0 0 1 0
0 4 1 0 0 0 0 0 7
0 0 7 0 0 0 3 0 0
第 2 轮循环,列

>>>>>>>>>>>Data from Vector<<<<<<<<<<
1 0 0 0 0 7 0 9 0
0 3 0 0 2 0 0 0 8
0 0 9 6 0 0 5 0 0
0 0 5 3 0 0 9 0 0
0 1 0 0 8 0 0 0 2
6 0 0 0 0 4 0 0 0
3 0 0 0 0 0 0 1 0
0 4 1 0 0 0 0 0 7
0 0 7 0 0 0 3 0 0

使用道具 举报

回复

您需要登录后才可以回帖 登录 | 注册

本版积分规则 发表回复

TOP技术积分榜 社区积分榜 徽章 团队 统计 知识索引树 积分竞拍 文本模式 帮助
  ITPUB首页 | ITPUB论坛 | 数据库技术 | 企业信息化 | 开发技术 | 微软技术 | 软件工程与项目管理 | IBM技术园地 | 行业纵向讨论 | IT招聘 | IT文档
  ChinaUnix | ChinaUnix博客 | ChinaUnix论坛
CopyRight 1999-2011 itpub.net All Right Reserved. 北京盛拓优讯信息技术有限公司版权所有 联系我们 未成年人举报专区 
京ICP备16024965号-8  北京市公安局海淀分局网监中心备案编号:11010802021510 广播电视节目制作经营许可证:编号(京)字第1149号
  
快速回复 返回顶部 返回列表