用PLSQL解决世界最难数独(不到1毫秒)
生活随笔
收集整理的這篇文章主要介紹了
用PLSQL解决世界最难数独(不到1毫秒)
小編覺得挺不錯的,現在分享給大家,幫大家做個參考.
? ? ? ? 以下兩段代碼分別用Oracle和PostgreSQL匿名塊解“世界最難數獨”,聲明代碼是別人寫的,這里只作為興趣記錄與學習。
? ? ? ? Oracle代碼出自http://www.itpub.net/thread-1071946-2-1.html,解題用時120毫秒。
DECLARETYPE 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 usedused_r t2_num; --- in this row whether the number has been usedused_a t2_num; --- in this area whether the number has been usedarea t2_num; ---- mapping from row, column to areav_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; BEGINs(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 LOOPFOR j IN 1..9 LOOPused_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 LOOPFOR j IN 1..9 LOOPk := TO_NUMBER(TRIM(SUBSTR(s(i),j,1)));IF k>0 THENused_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 LOOPFOR j IN 1..9 LOOPIF SUBSTR(s(i),j,1)=' ' THENv_cnt := v_cnt+1;v_cnt2 :=0;FOR k IN 1..9 LOOPIF 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 slotv_cnt2 := v_cnt2 +1;slot_value(v_cnt)(v_cnt2) := k;END IF;END LOOP;IF v_cnt2 = 0 THENRAISE_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 answerk := 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);ELSEslot_r(v_cnt) := i; ---- position of this slotslot_c(v_cnt) := j;END IF;END IF;END LOOP;END LOOP;---- initialize the value indexes of slotsv_idx := slot_value.FIRST;WHILE v_idx IS NOT NULL LOOPslot_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 LOOPWHILE slot_value_idx(v_idx) IS NOT NULL LOOP ---- try all values for this sloti := 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 slotused_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 answerslot_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 slotIF v_idx IS NULL THEN ----- no anwer foundDBMS_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));ELSEv_idx := slot_value.NEXT(v_idx);IF v_idx IS NULL THEN ----- all slots tried and found an answerv_idx := slot_value.FIRST;WHILE v_idx IS NOT NULL LOOPi := 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 LOOPDBMS_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 usedused_r int[][]:=array_fill(0,array[9,9]); --- in this row whether the number has been usedused_a int[][]:=array_fill(0,array[9,9]); --- in this area whether the number has been usedarea int[][]:=array_fill(0,array[9,9]); ---- mapping from row, column to areav_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]);BEGINs[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 LOOPFOR j IN 1..9 LOOParea[i][j] := (CEIL(i::numeric/3::numeric)-1)*3 + CEIL(j::numeric/3::numeric);END LOOP;END LOOP;FOR i IN 1..9 LOOPFOR j IN 1..9 LOOPk := (case when TRIM(SUBSTRing(s[i],j,1))='' then '0' else TRIM(SUBSTRing(s[i],j,1)) end)::int;IF k>0 THENused_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 LOOPFOR j IN 1..9 LOOPIF SUBSTRing(s[i],j,1)=' ' THENv_cnt := v_cnt+1;v_cnt2 :=0;FOR k IN 1..9 LOOPIF 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 slotv_cnt2 := v_cnt2 +1;slot_value[v_cnt][v_cnt2] := k;END IF;END LOOP;IF v_cnt2 = 0 THENRAISE 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 answerk := 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);ELSEslot_r[v_cnt] := i; ---- position of this slotslot_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 LOOPWHILE slot_value_idx[v_idx]<>0 LOOP ---- try all values for this sloti := 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 slotused_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 answerslot_value_idx[v_idx] := slot_value[1][1]; --- reset the index of this slotv_idx := v_idx-1; --- go back and release the last slotIF slot_value[v_idx][1] =0 THEN ----- no anwer foundraise 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;ELSEv_idx := v_idx+1;IF slot_value[v_idx][1] =0 THEN ----- all slots tried and found an answerv_idx := slot_value[1][1];WHILE slot_value[v_idx][1] <>0 and v_idx<>61 LOOPi := 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 LOOPraise notice '%',s[i];END LOOP;EXIT;END IF;END IF;END LOOP;END$$;百毫秒太慢了,祭出大招,1毫秒以內完成。
1. 編寫C程序實現數獨的DLX算法
以下代碼是我改寫自“SuDoKu_DLX_9*9”,因為要用PG來調用。(在我的環境中,PG中調用C函數比原始的C程序執行還快3-4倍)。源文件sudu.c內容如下:
#include "postgres.h" #include "fmgr.h"#define XSIZE 3 #define SIZE (XSIZE * XSIZE) //3*3=9 #define MAX_C (SIZE * SIZE * 4) //最大列 9*9*4=324 #define MAX_R (SIZE * SIZE * SIZE) //最大行 9*9*9=729 #define MAX_SUDOKU (SIZE * SIZE) //數獨矩陣大小 9*9 #define MAX_LINK (MAX_C * MAX_R) //鏈表最大范圍 324 * 729int L[MAX_LINK],R[MAX_LINK],U[MAX_LINK],D[MAX_LINK]; //抽象鏈表 int C[MAX_LINK],O[MAX_LINK],S[MAX_C],H[MAX_R]; //C&O代表列&行,S每一列的節點數,H每一行的第一個節點 int NodeNumber,RecordNumber; //用來指向節點 int state[MAX_SUDOKU],ans[MAX_SUDOKU],record[MAX_SUDOKU];bool input(char *buf); char *ret;//Dancing Links模版// void init(void); //Dancing Links的抽象鏈表初始化 void insert(int,int); //在鏈表的一個位置中添加標記 void removedata(int); //刪除一列,同時刪除這一列中的行 void resume(int); //恢復一列,同時恢復這一列中的行 //Dancing Links模版//void add(int,int,int); void build(void); bool dfs(int); void output(void);PG_MODULE_MAGIC; PG_FUNCTION_INFO_V1(sudu); Datum sudu(PG_FUNCTION_ARGS) {char *t = PG_GETARG_CSTRING(0);char *databuf = (char *)VARDATA(t);VarChar *funcValue = (VarChar *)palloc(MAX_SUDOKU + VARHDRSZ); SET_VARSIZE(funcValue, MAX_SUDOKU + VARHDRSZ);ret = (char *)VARDATA(funcValue); input(databuf);init();build();dfs(0);output();PG_RETURN_VARCHAR_P(funcValue); }bool input(char *buf) {int i;for(i=0;i<MAX_SUDOKU;i++){state[i]=buf[i]-'0';}return true; }//Dancing Links模版// void init(void) {for(int i=0;i<=MAX_C;i++){L[i]=i-1;R[i]=i+1;U[i]=i;D[i]=i;C[i]=i;O[i]=0;}L[0]=MAX_C;R[MAX_C]=0;NodeNumber=MAX_C+1;memset(S,0,sizeof(S));memset(H,0,sizeof(H)); }void insert(int i,int j) {if(H[i]){L[NodeNumber]=L[H[i]];R[NodeNumber]=H[i];L[R[NodeNumber]]=NodeNumber;R[L[NodeNumber]]=NodeNumber;}else{L[NodeNumber]=NodeNumber;R[NodeNumber]=NodeNumber;H[i]=NodeNumber;}U[NodeNumber]=U[j];D[NodeNumber]=j;U[D[NodeNumber]]=NodeNumber;D[U[NodeNumber]]=NodeNumber;C[NodeNumber]=j;O[NodeNumber]=i;S[j]++;NodeNumber++; }void add(int i,int j,int k) {int row=i*MAX_SUDOKU+j*SIZE+k;insert(row,i*SIZE+j+1);//注意insert(row,i*SIZE+k+MAX_SUDOKU);insert(row,j*SIZE+MAX_SUDOKU*2+k);insert(row,(i/XSIZE*XSIZE +j/XSIZE)*SIZE+MAX_SUDOKU*3+k); }void build(void) {int pos;for(int i=0;i<SIZE;i++)for(int j=0;j<SIZE;j++){pos=i*SIZE+j;if(state[pos]!=0){add(i,j,state[pos]);}else if(state[pos]==0){for(int k=1;k<=SIZE;k++){add(i,j,k);}}} }void removedata(int c) {L[R[c]]=L[c];R[L[c]]=R[c];for(int i=D[c];i!=c;i=D[i]){for(int j=R[i];j!=i;j=R[j]){U[D[j]]=U[j];D[U[j]]=D[j];S[C[j]]--;}} }void resume(int c) {for(int i=U[c];i!=c;i=U[i]){for(int j=L[i];j!=i;j=L[j]){U[D[j]]=j;D[U[j]]=j;S[C[j]]++;}}L[R[c]]=c;R[L[c]]=c; }bool dfs(int k) {int count=~(1<<31),c=0;if(!R[0]){RecordNumber=k;return true;}for(int i=R[0];i;i=R[i]){if(S[i]<count){count=S[i];c=i;if(count==1)break;}}removedata(c);for(int i=D[c];i!=c;i=D[i]){for(int j=R[i];j!=i;j=R[j]){removedata(C[j]);}record[k]=O[i];if(dfs(k+1))return true;for(int j=L[i];j!=i;j=L[j]){resume(C[j]);}}resume(c);return false; }void output(void) {int i,j,k=0;for(i=0;i<RecordNumber;i++){ans[(record[i]-1)/SIZE]=(record[i]-1)%SIZE+1;}for(i=0;i<SIZE;i++){for(j=0;j<SIZE;j++) {ret[k]='0' + ans[i*SIZE+j]; k=k+1;}} }2. 安裝postgresql12開發包
yum -y install postgresql12-devel3. 建立創建SQL函數的文件
sudu.sql.in文件內容如下:
create function sudu(text)returns varchar as 'MODULE_PATHNAME','sudu' language C strict;4. 創建Make文件
Makefile文件內容如下:
MODULES = sudu DATA_built = sudu.sqlPG_CONFIG = /usr/pgsql-12/bin/pg_config PGXS := $(shell $(PG_CONFIG) --pgxs) include $(PGXS)5. 編譯安裝C程序
# 編譯 make # 安裝 make install6.?創建SQL函數
psql -d test -f sudu.sql7. 用SQL查詢調用函數并格式化輸出
with t as (select sudu('800000000003600000070090200050007000000045700000100030001000068008500010090000400') a) select substr(a,1,9) from t union all select substr(a,10,9) from t union all select substr(a,19,9) from t union all select substr(a,28,9) from t union all select substr(a,37,9) from t union all select substr(a,46,9) from t union all select substr(a,55,9) from t union all select substr(a,64,9) from t union all select substr(a,73,9) from t;下面是見證奇跡的時刻:
總結
以上是生活随笔為你收集整理的用PLSQL解决世界最难数独(不到1毫秒)的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 思科网院计算机应用基础答案,计算机应用基
- 下一篇: mysql考勤查询,查询mysql中的考