用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解决世界最难数独的主要内容,如果未能解决你的问题,请参考以下文章