楼主: cuicg

[SQL] 用SQL或PL/SQL求解数独,替三思发帖。

[复制链接]
论坛徽章:
69
生肖徽章2007版:羊
日期:2008-11-14 14:42:19复活蛋
日期:2011-08-06 08:59:05ITPUB十周年纪念徽章
日期:2011-11-01 16:19:412012新春纪念徽章
日期:2012-01-04 11:49:542012新春纪念徽章
日期:2012-02-13 15:13:202012新春纪念徽章
日期:2012-02-13 15:13:202012新春纪念徽章
日期:2012-02-13 15:13:202012新春纪念徽章
日期:2012-02-13 15:13:202012新春纪念徽章
日期:2012-02-13 15:13:20版主4段
日期:2012-05-15 15:24:11
11#
发表于 2008-10-17 09:25 | 只看该作者
都在研究算法了啊...

使用道具 举报

回复
论坛徽章:
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
12#
发表于 2008-10-17 23:05 | 只看该作者
这种问题是很有趣的,因为它起点不高但要做好不容易。
最直观的想法就是用1-9去试每个空格了,但这样的效率肯定成问题。我现在的想法是加些简单推理排除一些,这样每个空就不必9个数字都去试了。

使用道具 举报

回复
论坛徽章:
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
13#
发表于 2008-10-18 03:18 | 只看该作者




  1. 第一位SQL牛人(其实他的MODEL已经和传统SQL天差地别了)的测试用例,他花了40秒,我的PL/SQL用时 1.5 秒。
  2. 第二位用SQLSERVER存储过程的,他用了一个号称史上最难的例子,花了5分钟20秒,我的PL/SQL用时 0.2 秒!

  3. PL/SQL全面胜出!


  4. DECLARE
  5.    TYPE t_num IS TABLE OF NUMBER INDEX BY BINARY_INTEGER;
  6.    TYPE t2_num IS TABLE OF t_num INDEX BY BINARY_INTEGER;

  7.    TYPE t_str IS TABLE OF VARCHAR2(10) INDEX BY BINARY_INTEGER;

  8.    s t_str;
  9.    
  10.    used_c t2_num;     --- in this column whether the number has been used
  11.    used_r t2_num;     --- in this row whether the number has been used
  12.    used_a t2_num;     --- in this area whether the number has been used
  13.    
  14.    area   t2_num;     ---- mapping from row, column to area
  15.    
  16.    v_cnt NUMBER :=0;
  17.    
  18.    slot_value t2_num;
  19.    slot_r t_num;
  20.    slot_c t_num;
  21.    
  22.    v_cnt2 NUMBER :=0;
  23.    
  24.    v_idx  NUMBER;
  25.    
  26.    slot_value_idx t_num;
  27.    
  28.    i  NUMBER;
  29.    j  NUMBER;
  30.    k  NUMBER;
  31. BEGIN
  32.    --- sample one
  33.    s(1):= ' 38     2' ;
  34.    s(2):= '  4 2  5 ' ;
  35.    s(3):= '     619 ' ;
  36.    s(4):= '   9  2  ' ;
  37.    s(5):= '    8    ' ;
  38.    s(6):= '4 2  1   ' ;
  39.    s(7):= ' 8       ' ;
  40.    s(8):= ' 9  3 8  ' ;
  41.    s(9):= '1     92 ' ;

  42.    ---- junsansi's sample
  43.    s(1):= '7  25  98' ;
  44.    s(2):= '  6    1 ' ;
  45.    s(3):= '   61 3  ' ;
  46.    s(4):= '9    1   ' ;
  47.    s(5):= '    8 4 9' ;
  48.    s(6):= '  75 28 1' ;
  49.    s(7):= ' 94  3   ' ;
  50.    s(8):= '    4923 ' ;
  51.    s(9):= '61     4 ' ;

  52.    --- AlEscargot, the most complex SUDOKU puzzle ever
  53.    s(1):= '1    7 9 ' ;
  54.    s(2):= ' 3  2   8' ;
  55.    s(3):= '  96  5  ' ;
  56.    s(4):= '  53  9  ' ;
  57.    s(5):= ' 1  8   2' ;
  58.    s(6):= '6    4   ' ;
  59.    s(7):= '3      1 ' ;
  60.    s(8):= ' 4      7' ;
  61.    s(9):= '  7   3  ' ;


  62.    FOR i IN 1..9 LOOP
  63.        FOR j IN 1..9 LOOP
  64.            used_c(i)(j) := 0;
  65.            used_r(i)(j) := 0;
  66.            used_a(i)(j) := 0;
  67.            
  68.            area(i)(j) := (CEIL(i/3)-1)*3 + CEIL(j/3);
  69.        END LOOP;
  70.    END LOOP;

  71.    FOR i IN 1..9 LOOP
  72.        FOR j IN 1..9 LOOP
  73.            k := TO_NUMBER(TRIM(SUBSTR(s(i),j,1)));
  74.            IF k>0 THEN
  75.               used_c(j)(k) :=1;
  76.               used_r(i)(k) :=1;
  77.               used_a(area(i)(j))(k) :=1;
  78.            END IF;
  79.        END LOOP;
  80.    END LOOP;
  81.    
  82.    FOR i IN 1..9 LOOP
  83.        FOR j IN 1..9 LOOP
  84.            IF SUBSTR(s(i),j,1)=' ' THEN
  85.               v_cnt := v_cnt+1;
  86.               
  87.               v_cnt2 :=0;
  88.               
  89.               FOR k IN 1..9 LOOP
  90.                   IF used_c(j)(k) = 0 AND used_r(i)(k) = 0 AND used_a(area(i)(j))(k) = 0 THEN  ---- possible value found for this slot
  91.                      v_cnt2 := v_cnt2 +1;
  92.                      slot_value(v_cnt)(v_cnt2) := k;
  93.                   END IF;
  94.               END LOOP;
  95.               
  96.               IF v_cnt2 = 0 THEN
  97.                  RAISE_APPLICATION_ERROR(-20001,'invalid sudoku at '||i||','||j);
  98.               END IF;
  99.               
  100.               IF v_cnt2 = 1 THEN        ---- there's only one value for this slot, it's the answer
  101.                  k := slot_value(v_cnt)(1);
  102.                  used_c(j)(k) :=1;
  103.                  used_r(i)(k) :=1;
  104.                  used_a(area(i)(j))(k) :=1;
  105.                  v_cnt := v_cnt - 1;
  106.                  
  107.                  s(i) := SUBSTR(s(i),1,j-1) ||k|| SUBSTR(s(i),j+1);

  108.               ELSE
  109.                  slot_r(v_cnt) := i;       ---- position of this slot
  110.                  slot_c(v_cnt) := j;
  111.               END IF;
  112.               
  113.            END IF;
  114.        END LOOP;
  115.    END LOOP;
  116.    
  117.    ---- initialize the value indexes of slots
  118.    v_idx := slot_value.FIRST;
  119.    
  120.    WHILE v_idx IS NOT NULL LOOP
  121.          slot_value_idx(v_idx) := slot_value(v_idx).FIRST;
  122.          
  123.          v_idx := slot_value.NEXT(v_idx);
  124.    END LOOP;
  125.    
  126.    v_idx := slot_value.FIRST;
  127.    
  128.    WHILE v_idx IS NOT NULL LOOP
  129.          WHILE slot_value_idx(v_idx) IS NOT NULL LOOP      ---- try all values for this slot
  130.                i := slot_r(v_idx);
  131.                j := slot_c(v_idx);
  132.                k := slot_value(v_idx)(slot_value_idx(v_idx));
  133.                
  134.                IF used_c(j)(k) = 0 AND used_r(i)(k) = 0 AND used_a(area(i)(j))(k) = 0 THEN  ---- possible value found for this slot
  135.                   used_c(j)(k) := 1;
  136.                   used_r(i)(k) := 1;
  137.                   used_a(area(i)(j))(k) :=1;
  138.                   EXIT;
  139.                END IF;
  140.                
  141.                slot_value_idx(v_idx) := slot_value(v_idx).NEXT(slot_value_idx(v_idx));
  142.          END LOOP;
  143.          
  144.          IF slot_value_idx(v_idx) IS NULL THEN   ---- all values tried but none is the answer
  145.             slot_value_idx(v_idx) := slot_value(v_idx).FIRST;   --- reset the index of this slot
  146.             
  147.             v_idx := slot_value.PRIOR(v_idx);     --- go back and release the last slot
  148.             IF v_idx IS NULL THEN      ----- no anwer found
  149.                DBMS_OUTPUT.PUT_LINE('No Answer Found!');
  150.                EXIT;
  151.             END IF;
  152.             
  153.             i := slot_r(v_idx);
  154.             j := slot_c(v_idx);
  155.             k := slot_value(v_idx)(slot_value_idx(v_idx));
  156.             
  157.             used_c(j)(k) := 0;
  158.             used_r(i)(k) := 0;
  159.             used_a(area(i)(j))(k) :=0;
  160.             
  161.             slot_value_idx(v_idx) := slot_value(v_idx).NEXT(slot_value_idx(v_idx));
  162.             
  163.          ELSE
  164.             v_idx := slot_value.NEXT(v_idx);
  165.             
  166.             IF v_idx IS NULL THEN      ----- all slots tried and found an answer

  167.                v_idx := slot_value.FIRST;
  168.                
  169.                WHILE v_idx IS NOT NULL LOOP
  170.                      i := slot_r(v_idx);
  171.                      j := slot_c(v_idx);
  172.                      k := slot_value(v_idx)(slot_value_idx(v_idx));
  173.                      s(i) := SUBSTR(s(i),1,j-1) || k || SUBSTR(s(i),j+1);
  174.    
  175.                      v_idx := slot_value.NEXT(v_idx);
  176.                END LOOP;
  177.                
  178.                DBMS_OUTPUT.PUT_LINE('Answer Found:');
  179.                FOR i IN 1..9 LOOP
  180.                    DBMS_OUTPUT.PUT_LINE(s(i));
  181.                END LOOP;
  182.                
  183.                EXIT;

  184.             END IF;
  185.             
  186.          END IF;
  187.    END LOOP;
  188.    
  189. END;
  190. /




复制代码



[ 本帖最后由 newkid 于 2008-10-18 05:24 编辑 ]

使用道具 举报

回复
论坛徽章:
1088
金色在线徽章
日期:2007-04-25 04:02:08金色在线徽章
日期:2007-06-29 04:02:43金色在线徽章
日期:2007-03-11 04:02:02在线时间
日期:2007-04-11 04:01:02在线时间
日期:2007-04-12 04:01:02在线时间
日期:2007-03-07 04:01:022008版在线时间
日期:2010-05-01 00:01:152008版在线时间
日期:2011-05-01 00:01:342008版在线时间
日期:2008-06-03 11:59:43ITPUB年度最佳技术原创精华奖
日期:2013-03-22 13:18:30
14#
发表于 2008-10-19 11:48 | 只看该作者
newkid,哥哥你也太强了吧,小弟佩服之至!

使用道具 举报

回复
论坛徽章:
281
2015年新春福章
日期:2015-03-06 11:57:312012新春纪念徽章
日期:2012-02-13 15:12:252012新春纪念徽章
日期:2012-01-04 11:51:22蛋疼蛋
日期:2011-12-29 07:37:22迷宫蛋
日期:2011-12-26 14:19:41茶鸡蛋
日期:2011-11-17 09:20:52茶鸡蛋
日期:2011-11-10 22:42:38ITPUB十周年纪念徽章
日期:2011-11-01 16:21:15茶鸡蛋
日期:2011-10-24 09:48:48ITPUB十周年纪念徽章
日期:2011-09-27 16:30:47
15#
发表于 2008-10-20 09:16 | 只看该作者
原帖由 newkid 于 2008-10-18 03:18 发表




第一位SQL牛人(其实他的MODEL已经和传统SQL天差地别了)的测试用例,他花了40秒,我的PL/SQL用时 1.5 秒。
第二位用SQLSERVER存储过程的,他用了一个号称史上最难的例子,花了5分钟20秒,我的PL/SQL用时 0.2 秒!

PL/SQL全面胜出!


DECLARE
   TYPE t_num IS TABLE OF NUMBER INDEX BY BINARY_INTEGER;
   TYPE t2_num IS TABLE OF t_num INDEX BY BINARY_INTEGER;

   TYPE t_str IS TABLE OF VARCHAR2(10) INDEX BY BINARY_INTEGER;

   s t_str;
   
   used_c t2_num;     --- in this column whether the number has been used
   used_r t2_num;     --- in this row whether the number has been used
   used_a t2_num;     --- in this area whether the number has been used
   
   area   t2_num;     ---- mapping from row, column to area
   
   v_cnt NUMBER :=0;
   
   slot_value t2_num;
   slot_r t_num;
   slot_c t_num;
   
   v_cnt2 NUMBER :=0;
   
   v_idx  NUMBER;
   
   slot_value_idx t_num;
   
   i  NUMBER;
   j  NUMBER;
   k  NUMBER;
BEGIN
   --- sample one
   s(1):= ' 38     2' ;
   s(2):= '  4 2  5 ' ;
   s(3):= '     619 ' ;
   s(4):= '   9  2  ' ;
   s(5):= '    8    ' ;
   s(6):= '4 2  1   ' ;
   s(7):= ' 8       ' ;
   s(8):= ' 9  3 8  ' ;
   s(9):= '1     92 ' ;

   ---- junsansi's sample
   s(1):= '7  25  98' ;
   s(2):= '  6    1 ' ;
   s(3):= '   61 3  ' ;
   s(4):= '9    1   ' ;
   s(5):= '    8 4 9' ;
   s(6):= '  75 28 1' ;
   s(7):= ' 94  3   ' ;
   s(8):= '    4923 ' ;
   s(9):= '61     4 ' ;

   --- AlEscargot, the most complex SUDOKU puzzle ever
   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  ' ;


   FOR i IN 1..9 LOOP
       FOR j IN 1..9 LOOP
           used_c(i)(j) := 0;
           used_r(i)(j) := 0;
           used_a(i)(j) := 0;
           
           area(i)(j) := (CEIL(i/3)-1)*3 + CEIL(j/3);
       END LOOP;
   END LOOP;

   FOR i IN 1..9 LOOP
       FOR j IN 1..9 LOOP
           k := TO_NUMBER(TRIM(SUBSTR(s(i),j,1)));
           IF k>0 THEN
              used_c(j)(k) :=1;
              used_r(i)(k) :=1;
              used_a(area(i)(j))(k) :=1;
           END IF;
       END LOOP;
   END LOOP;
   
   FOR i IN 1..9 LOOP
       FOR j IN 1..9 LOOP
           IF SUBSTR(s(i),j,1)=' ' THEN
              v_cnt := v_cnt+1;
              
              v_cnt2 :=0;
              
              FOR k IN 1..9 LOOP
                  IF used_c(j)(k) = 0 AND used_r(i)(k) = 0 AND used_a(area(i)(j))(k) = 0 THEN  ---- possible value found for this slot
                     v_cnt2 := v_cnt2 +1;
                     slot_value(v_cnt)(v_cnt2) := k;
                  END IF;
              END LOOP;
              
              IF v_cnt2 = 0 THEN
                 RAISE_APPLICATION_ERROR(-20001,'invalid sudoku at '||i||','||j);
              END IF;
              
              IF v_cnt2 = 1 THEN        ---- there's only one value for this slot, it's the answer
                 k := slot_value(v_cnt)(1);
                 used_c(j)(k) :=1;
                 used_r(i)(k) :=1;
                 used_a(area(i)(j))(k) :=1;
                 v_cnt := v_cnt - 1;
                 
                 s(i) := SUBSTR(s(i),1,j-1) ||k|| SUBSTR(s(i),j+1);

              ELSE
                 slot_r(v_cnt) := i;       ---- position of this slot
                 slot_c(v_cnt) := j;
              END IF;
              
           END IF;
       END LOOP;
   END LOOP;
   
   ---- initialize the value indexes of slots
   v_idx := slot_value.FIRST;
   
   WHILE v_idx IS NOT NULL LOOP
         slot_value_idx(v_idx) := slot_value(v_idx).FIRST;
         
         v_idx := slot_value.NEXT(v_idx);
   END LOOP;
   
   v_idx := slot_value.FIRST;
   
   WHILE v_idx IS NOT NULL LOOP
         WHILE slot_value_idx(v_idx) IS NOT NULL LOOP      ---- try all values for this slot
               i := slot_r(v_idx);
               j := slot_c(v_idx);
               k := slot_value(v_idx)(slot_value_idx(v_idx));
               
               IF used_c(j)(k) = 0 AND used_r(i)(k) = 0 AND used_a(area(i)(j))(k) = 0 THEN  ---- possible value found for this slot
                  used_c(j)(k) := 1;
                  used_r(i)(k) := 1;
                  used_a(area(i)(j))(k) :=1;
                  EXIT;
               END IF;
               
               slot_value_idx(v_idx) := slot_value(v_idx).NEXT(slot_value_idx(v_idx));
         END LOOP;
         
         IF slot_value_idx(v_idx) IS NULL THEN   ---- all values tried but none is the answer
            slot_value_idx(v_idx) := slot_value(v_idx).FIRST;   --- reset the index of this slot
            
            v_idx := slot_value.PRIOR(v_idx);     --- go back and release the last slot
            IF v_idx IS NULL THEN      ----- no anwer found
               DBMS_OUTPUT.PUT_LINE('No Answer Found!');
               EXIT;
            END IF;
            
            i := slot_r(v_idx);
            j := slot_c(v_idx);
            k := slot_value(v_idx)(slot_value_idx(v_idx));
            
            used_c(j)(k) := 0;
            used_r(i)(k) := 0;
            used_a(area(i)(j))(k) :=0;
            
            slot_value_idx(v_idx) := slot_value(v_idx).NEXT(slot_value_idx(v_idx));
            
         ELSE
            v_idx := slot_value.NEXT(v_idx);
            
            IF v_idx IS NULL THEN      ----- all slots tried and found an answer

               v_idx := slot_value.FIRST;
               
               WHILE v_idx IS NOT NULL LOOP
                     i := slot_r(v_idx);
                     j := slot_c(v_idx);
                     k := slot_value(v_idx)(slot_value_idx(v_idx));
                     s(i) := SUBSTR(s(i),1,j-1) || k || SUBSTR(s(i),j+1);
   
                     v_idx := slot_value.NEXT(v_idx);
               END LOOP;
               
               DBMS_OUTPUT.PUT_LINE('Answer Found:');
               FOR i IN 1..9 LOOP
                   DBMS_OUTPUT.PUT_LINE(s(i));
               END LOOP;
               
               EXIT;

            END IF;
            
         END IF;
   END LOOP;
   
END;
/







使用道具 举报

回复
论坛徽章:
40
授权会员
日期:2009-03-04 17:06:25最佳人气徽章
日期:2013-03-19 17:24:25SQL极客
日期:2013-12-09 14:13:35优秀写手
日期:2013-12-18 09:29:09ITPUB元老
日期:2015-03-04 13:33:34白羊座
日期:2016-03-11 13:49:34乌索普
日期:2017-11-17 11:40:00
16#
发表于 2008-10-20 09:47 | 只看该作者
newkid  真是强悍啊  

使用道具 举报

回复
论坛徽章:
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
17#
发表于 2008-10-20 22:25 | 只看该作者
这个解法其实很简单,类似“八皇后问题”,大家上数据结构课都做过这个练习吧?我一开始都不相信效果有这么好。
等会我有空再稍微优化一下。

使用道具 举报

回复
论坛徽章:
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
18#
发表于 2008-10-21 04:23 | 只看该作者




  1. 下面的另一写法是我试图进行优化的结果,我本来打算借鉴ORACLE连接表时用小表驱动大表的思路,在进入循环之前做个排序。试验证明对很多题型是有效的,我测的都在0.X或0.0X秒,但对那个AlEscargot题反而从0.15秒上升到5.x 秒。看来运气还是很重要,有时候答案很快就碰上了。

  2. 这个新写法利用SQL对内存数组(嵌套表)进行排序,总算和ORACLE有了点关系,算是一种技巧吧:
  3. SELECT CAST(MULTISET(SELECT * FROM TABLE(CAST(empty_slots AS table_slot)) ORDER BY cnt,r,c) AS table_slot)
  4.   INTO empty_slots
  5.   FROM DUAL;

  6. 我又对Anton Scheffer老兄的SQL作了些测试,有时候他的SQL也是很快的,但他自己的第一个例子却要花40来秒,还是运气问题。


  7. CREATE OR REPLACE TYPE t_num IS TABLE OF NUMBER
  8. /

  9. CREATE OR REPLACE TYPE type_slot AS OBJECT  (
  10.             r           NUMBER
  11.            ,c           NUMBER
  12.            ,cnt         NUMBER
  13.            ,idx         NUMBER
  14.   )
  15. /

  16. CREATE OR REPLACE TYPE table_slot AS TABLE OF type_slot
  17. /


  18. DECLARE
  19.    TYPE t2_num IS TABLE OF t_num INDEX BY BINARY_INTEGER;
  20.    TYPE t3_num IS TABLE OF t2_num INDEX BY BINARY_INTEGER;
  21.    TYPE t_str IS TABLE OF VARCHAR2(10) INDEX BY BINARY_INTEGER;

  22.    s t_str;
  23.    
  24.    used_c t2_num;     --- in this column whether the number has been used
  25.    used_r t2_num;     --- in this row whether the number has been used
  26.    used_a t2_num;     --- in this area whether the number has been used
  27.    
  28.    area   t2_num;     ---- mapping from row, column to area
  29.    
  30.    slot_values t3_num;
  31.    
  32.    v_idx  NUMBER;
  33.    
  34.    i  NUMBER;
  35.    j  NUMBER;
  36.    k  NUMBER;
  37.    
  38.    empty_set  t_num := t_num(0,0,0,0,0,0,0,0,0);
  39.    
  40.    empty_slots table_slot := table_slot();
  41.    
  42.    v_continue NUMBER;
  43.    v_del      NUMBER;

  44. BEGIN

  45.    ---- junsansi's sample
  46.    s(1):= '7  25  98' ;
  47.    s(2):= '  6    1 ' ;
  48.    s(3):= '   61 3  ' ;
  49.    s(4):= '9    1   ' ;
  50.    s(5):= '    8 4 9' ;
  51.    s(6):= '  75 28 1' ;
  52.    s(7):= ' 94  3   ' ;
  53.    s(8):= '    4923 ' ;
  54.    s(9):= '61     4 ' ;

  55.    --- AlEscargot, the most complex SUDOKU puzzle ever
  56.    s(1):= '1    7 9 ' ;
  57.    s(2):= ' 3  2   8' ;
  58.    s(3):= '  96  5  ' ;
  59.    s(4):= '  53  9  ' ;
  60.    s(5):= ' 1  8   2' ;
  61.    s(6):= '6    4   ' ;
  62.    s(7):= '3      1 ' ;
  63.    s(8):= ' 4      7' ;
  64.    s(9):= '  7   3  ' ;

  65.    --- sample one
  66.    s(1):= '3 58  176';
  67.    s(2):= '1   5   2';
  68.    s(3):= '      549';
  69.    s(4):= ' 52 986  ';
  70.    s(5):= '43  12  7';
  71.    s(6):= '   5  4  ';
  72.    s(7):= '5   6 894';
  73.    s(8):= ' 69 7   5';
  74.    s(9):= '   9   61';


  75.    FOR i IN 1..9 LOOP
  76.        used_r(i) := empty_set;
  77.        used_c(i) := empty_set;
  78.        used_a(i) := empty_set;
  79.        area(i)   := empty_set;
  80.       
  81.        FOR j IN 1..9 LOOP
  82.            area(i)(j) := (CEIL(i/3)-1)*3 + CEIL(j/3);
  83.            slot_values(i)(j) := empty_set;
  84.        END LOOP;
  85.    END LOOP;

  86.    FOR i IN 1..9 LOOP
  87.        FOR j IN 1..9 LOOP
  88.            k := TO_NUMBER(TRIM(SUBSTR(s(i),j,1)));
  89.            IF k>0 THEN
  90.               used_r(i)(k) :=1;
  91.               used_c(j)(k) :=1;
  92.               used_a(area(i)(j))(k) :=1;
  93.               
  94.               FOR v_del IN 1..9 LOOP
  95.                   slot_values(i)(v_del).DELETE(k);
  96.                   slot_values(v_del)(j).DELETE(k);
  97.                   slot_values((CEIL(i/3)-1)*3+CEIL(v_del/3))((CEIL(j/3)-1)*3+MOD(v_del-1,3)+1).DELETE(k);
  98.               END LOOP;

  99.            ELSE

  100.               empty_slots.EXTEND;
  101.               empty_slots(empty_slots.COUNT) := type_slot(i,j,0,0);
  102.            END IF;
  103.        END LOOP;
  104.    END LOOP;

  105.    v_continue :=1;
  106.    WHILE v_continue = 1 AND empty_slots.COUNT>0 LOOP
  107.          v_continue := 0;
  108.          v_idx := empty_slots.FIRST;
  109.          WHILE v_idx IS NOT NULL LOOP
  110.                empty_slots(v_idx).cnt := slot_values(empty_slots(v_idx).r)(empty_slots(v_idx).c).COUNT;
  111.                IF empty_slots(v_idx).cnt = 0 THEN
  112.                   RAISE_APPLICATION_ERROR(-20001,'invalid sudoku at '||empty_slots(v_idx).r||','||empty_slots(v_idx).c);
  113.                ELSIF empty_slots(v_idx).cnt = 1 THEN
  114.                   i := empty_slots(v_idx).r;
  115.                   j := empty_slots(v_idx).c;
  116.                   k := slot_values(i)(j).FIRST;
  117.                   used_r(i)(k) :=1;
  118.                   used_c(j)(k) :=1;
  119.                   used_a(area(i)(j))(k) :=1;
  120.                   
  121.                   FOR v_del IN 1..9 LOOP
  122.                       slot_values(i)(v_del).DELETE(k);
  123.                       slot_values(v_del)(j).DELETE(k);
  124.                       slot_values((CEIL(i/3)-1)*3+CEIL(v_del/3))((CEIL(j/3)-1)*3+MOD(v_del-1,3)+1).DELETE(k);
  125.                   END LOOP;
  126.                   
  127.                   v_continue := 1;
  128.                   v_del := v_idx;
  129.                   s(i) := SUBSTR(s(i),1,j-1) ||k|| SUBSTR(s(i),j+1);
  130.                ELSE
  131.                   v_del := 0;
  132.                END IF;
  133.                
  134.                v_idx := empty_slots.NEXT(v_idx);
  135.                
  136.                IF v_del>0 THEN
  137.                   empty_slots.DELETE(v_del);
  138.                END IF;
  139.                
  140.          END LOOP;
  141.    END LOOP;

  142.    v_continue := 1;

  143.    IF empty_slots.COUNT>0 THEN
  144.       ----- 借鉴小表驱动大表的思路进行排序预处理。实际应用效果不佳,此SELECT可以去掉。
  145.       --SELECT CAST(MULTISET(SELECT * FROM TABLE(CAST(empty_slots AS table_slot)) ORDER BY cnt,r,c) AS table_slot)
  146.       --  INTO empty_slots
  147.       --  FROM DUAL;
  148.    
  149.       v_idx := empty_slots.FIRST;
  150.       empty_slots(v_idx).idx := slot_values(empty_slots(v_idx).r)(empty_slots(v_idx).c).FIRST;
  151.       
  152.       WHILE v_idx IS NOT NULL LOOP
  153.             WHILE empty_slots(v_idx).idx IS NOT NULL LOOP      ---- try all values for this slot
  154.                   i := empty_slots(v_idx).r;
  155.                   j := empty_slots(v_idx).c;
  156.                   k := empty_slots(v_idx).idx;   
  157.    
  158.                   IF used_c(j)(k) = 0 AND used_r(i)(k) = 0 AND used_a(area(i)(j))(k) = 0 THEN  ---- possible value found for this slot
  159.                      used_c(j)(k) := 1;
  160.                      used_r(i)(k) := 1;
  161.                      used_a(area(i)(j))(k) :=1;
  162.                      EXIT;
  163.                   END IF;
  164.                   
  165.                   empty_slots(v_idx).idx := slot_values(i)(j).NEXT(empty_slots(v_idx).idx);
  166.             END LOOP;
  167.             
  168.             IF empty_slots(v_idx).idx IS NULL THEN   ---- all values tried but none is the answer
  169.                -- slot_value_idx(v_idx) := slot_value(v_idx).FIRST;   --- reset the index of this slot
  170.                
  171.                v_idx := empty_slots.PRIOR(v_idx);     --- go back and release the last slot
  172.                IF v_idx IS NULL THEN      ----- no anwer found
  173.                   v_continue := 0;
  174.                   EXIT;
  175.                END IF;
  176.    
  177.                i := empty_slots(v_idx).r;
  178.                j := empty_slots(v_idx).c;
  179.                k := empty_slots(v_idx).idx;      
  180.                
  181.                used_c(j)(k) := 0;
  182.                used_r(i)(k) := 0;
  183.                used_a(area(i)(j))(k) :=0;
  184.                
  185.                empty_slots(v_idx).idx := slot_values(i)(j).NEXT(empty_slots(v_idx).idx);
  186.                
  187.             ELSE
  188.                v_idx := empty_slots.NEXT(v_idx);
  189.                
  190.                IF v_idx IS NULL THEN      ----- all slots tried and found an answer
  191.                   EXIT;
  192.                END IF;
  193.                empty_slots(v_idx).idx := slot_values(empty_slots(v_idx).r)(empty_slots(v_idx).c).FIRST;
  194.             END IF;
  195.       END LOOP;
  196.    
  197.    END IF;

  198.    IF v_continue = 1 THEN
  199.       v_idx := empty_slots.FIRST;
  200.       
  201.       WHILE v_idx IS NOT NULL LOOP
  202.             i := empty_slots(v_idx).r;
  203.             j := empty_slots(v_idx).c;
  204.             k := empty_slots(v_idx).idx;      
  205.             s(i) := SUBSTR(s(i),1,j-1) || k || SUBSTR(s(i),j+1);
  206.    
  207.             v_idx := empty_slots.NEXT(v_idx);
  208.       END LOOP;
  209.       
  210.       DBMS_OUTPUT.PUT_LINE('Answer Found:');
  211.       FOR i IN 1..9 LOOP
  212.           DBMS_OUTPUT.PUT_LINE(s(i));
  213.       END LOOP;
  214.    ELSE
  215.       DBMS_OUTPUT.PUT_LINE('No Answer Found!');
  216.    
  217.    END IF;

  218. END;
  219. /

  220. 后记:去这里下载了160高级题,经过比较排序和不排序预处理的解题速度,竟然是不排序的更快,唉白作优化了。
  221. [url]http://www.sudokufans.org.cn/shudo/download/160hard.rar[/url]

复制代码



[ 本帖最后由 newkid 于 2008-10-21 06:40 编辑 ]

使用道具 举报

回复
论坛徽章:
7
2010数据库技术大会纪念徽章
日期:2010-05-13 09:34:22
19#
 楼主| 发表于 2008-10-21 08:30 | 只看该作者
newkid 大师难道不睡觉么?小弟对你的渊博知识佩服的五体投地,对你的专注精神更加敬仰!

使用道具 举报

回复
论坛徽章:
33
劳斯莱斯
日期:2013-08-08 14:01:23三菱
日期:2013-09-28 10:16:06一汽
日期:2013-11-19 17:01:11凯迪拉克
日期:2013-12-07 17:11:282014年新春福章
日期:2014-02-18 16:42:02马上有房
日期:2014-02-18 16:42:02itpub13周年纪念徽章
日期:2014-09-27 14:20:21itpub13周年纪念徽章
日期:2014-10-08 15:13:38懒羊羊
日期:2015-03-04 14:52:112015年新春福章
日期:2015-03-06 11:58:18
20#
发表于 2008-10-21 09:56 | 只看该作者
原帖由 cuicg 于 2008-10-21 08:30 发表
newkid 大师难道不睡觉么?小弟对你的渊博知识佩服的五体投地,对你的专注精神更加敬仰!

时差.
人家正是上班时间,能睡觉么?

使用道具 举报

回复

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

本版积分规则 发表回复

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