用PLSQL解决世界最难数独

Posted wzy0623

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了用PLSQL解决世界最难数独相关的知识,希望对你有一定的参考价值。

        以下两段代码分别用Oracle和PostgreSQL匿名块解“世界最难数独”,声明代码是别人写的,这里只作为兴趣记录与学习。

        Oracle代码出自http://www.itpub.net/thread-1071946-2-1.html,解题用时120毫秒。

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
   s(1):= '8        ';
   s(2):= '  36     ';
   s(3):= ' 7  9 2  ';
   s(4):= ' 5   7   ';
   s(5):= '    457  ';
   s(6):= '   1   3 ';
   s(7):= '  1    68';
   s(8):= '  85   1 ';
   s(9):= ' 9    4  ';


   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;
         -- DBMS_OUTPUT.PUT_LINE(v_idx||','||slot_value(v_idx).FIRST);
         v_idx := slot_value.NEXT(v_idx);
		 -- DBMS_OUTPUT.PUT_LINE(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));
			   -- DBMS_OUTPUT.PUT_LINE(v_idx||','||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
            -- DBMS_OUTPUT.PUT_LINE(v_idx||','||slot_value(v_idx).FIRST);
            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;
/

        PG的代码是另一位大神改写Oracle的,解题用时800毫秒。

DO $$DECLARE 

   i int;
   j int;
   k  int;

   s _varchar(10);
   
   used_c int[][]:=array_fill(0,array[9,9]);     --- in this column whether the number has been used
   used_r int[][]:=array_fill(0,array[9,9]);     --- in this row whether the number has been used
   used_a int[][]:=array_fill(0,array[9,9]);     --- in this area whether the number has been used
   
   area int[][]:=array_fill(0,array[9,9]);     ---- mapping from row, column to area
   
   v_cnt int :=0;
   
   slot_value int[][]:=array_fill(0,array[100,100]);
   
   slot_r int[];
   slot_c int[];
   
   v_cnt2 int :=0;
   
   v_idx  int;
   
   slot_value_idx int[]=array_fill(1,array[60]);
   
BEGIN

   s[1]:= '8        ';
   s[2]:= '  36     ';
   s[3]:= ' 7  9 2  ';
   s[4]:= ' 5   7   ';
   s[5]:= '    457  ';
   s[6]:= '   1   3 ';
   s[7]:= '  1    68';
   s[8]:= '  85   1 ';
   s[9]:= ' 9    4  ';

   FOR i IN 1..9 LOOP
       FOR j IN 1..9 LOOP
           area[i][j] := (CEIL(i::numeric/3::numeric)-1)*3 + CEIL(j::numeric/3::numeric);
       END LOOP;
   END LOOP;

   FOR i IN 1..9 LOOP
       FOR j IN 1..9 LOOP
           k := (case when TRIM(SUBSTRing(s[i],j,1))='' then '0' else TRIM(SUBSTRing(s[i],j,1)) end)::int;
           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 SUBSTRing(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 EXCEPTION '-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] := SUBSTRing(s[i],1,j-1) ||k|| SUBSTRing(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;
   
   v_idx := slot_value[1][1];

   WHILE slot_value[v_idx][1] <>0 LOOP
         WHILE slot_value_idx[v_idx]<>0 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] := case when slot_value[v_idx][slot_value_idx[v_idx]+1] = 0 then 0 else slot_value_idx[v_idx]+1 end;

         END LOOP;

         IF slot_value_idx[v_idx]=0 THEN   ---- all values tried but none is the answer
            
			slot_value_idx[v_idx] := slot_value[1][1];   --- reset the index of this slot
            v_idx := v_idx-1;     --- go back and release the last slot

            IF slot_value[v_idx][1] =0 THEN      ----- no anwer found
			   raise notice '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] := case when slot_value[v_idx][slot_value_idx[v_idx]+1] = 0 then 0 else slot_value_idx[v_idx]+1 end;
			 
         ELSE
            v_idx := v_idx+1;
            
            IF slot_value[v_idx][1] =0 THEN      ----- all slots tried and found an answer

               v_idx := slot_value[1][1];

               WHILE slot_value[v_idx][1] <>0 and v_idx<>61 LOOP
			   
                     i := slot_r[v_idx];
                     j := slot_c[v_idx];
                     k := slot_value[v_idx][slot_value_idx[v_idx]];
                     s[i] := SUBSTRing(s[i],1,j-1) || k || SUBSTRing(s[i],j+1);
   
                     v_idx := v_idx+1;
					 
               END LOOP;
               
               raise notice 'Answer Found:';
               FOR i IN 1..9 LOOP
                   raise notice '%',s[i];
               END LOOP;
               
               EXIT;

            END IF;
            
         END IF;

   END LOOP;
   
END$$;

以上是关于用PLSQL解决世界最难数独的主要内容,如果未能解决你的问题,请参考以下文章

codevs 2924 数独挑战

洛谷 1784数独

数独(luogu 1784)

洛谷—— P1784 数独

洛谷 P1784 数独[DFS/回溯]

用 C++ 解决数独难题