用pl/sql解決芬蘭數學家因卡拉設計的最難數獨
最近看朋友在玩消滅星星(pop start),想起當年還不是彩屏手機的時候,我喜歡玩數獨遊戲.數獨(Sudoku)又稱九宮格,據說來頭很大,起源於河圖洛書,相信大家都有玩過.百度九宮格,
百度百科最後有提到芬蘭數學家因卡拉花費3個月設計出了世界上迄今難度最大的九宮格遊戲,而且它只有一個答案,就想挑戰一下.水平有限花了2天時間問題解決,速度還行2秒內就
能解答,發現還是比較簡單的,大概的分為兩段,第一段是找到唯一的那些值填上做為永久值,然後再找唯一值做為永久值,直到找不到唯一值.第二段透過回溯,把可能的值按順序嘗試,不
合適就回退,至到找到最終合適的.
以下指令碼及解釋:
DECLARE
--定義數獨行列
TYPE T_ROW IS VARRAY(9) OF NUMBER;
TYPE T_COL_ROW IS VARRAY(9) OF T_ROW;
--初始化數獨
v_init T_COL_ROW := T_COL_ROW(T_ROW(8, 0, 0, 0, 0, 0, 0, 0, 0),
T_ROW(0, 0, 3, 6, 0, 0, 0, 0, 0),
T_ROW(0, 7, 0, 0, 9, 0, 2, 0, 0),
T_ROW(0, 5, 0, 0, 0, 7, 0, 0, 0),
T_ROW(0, 0, 0, 0, 4, 5, 7, 0, 0),
T_ROW(0, 0, 0, 1, 0, 0, 0, 3, 0),
T_ROW(0, 0, 1, 0, 0, 0, 0, 6, 8),
T_ROW(0, 0, 8, 5, 0, 0, 0, 1, 0),
T_ROW(0, 9, 0, 0, 0, 0, 4, 0, 0));
--用以存放答案
v_result T_COL_ROW;
--定義一個用以計算小宮裡面的同組資料的位置
TYPE T_X IS VARRAY(2) OF NUMBER;
TYPE T_Y IS VARRAY(3) OF T_X;
--用以根據相對位置,算出同組資料的絕對位置
v_xy T_Y := T_Y(T_X(1, 2), T_X(-1, 1), T_X(-1, -2));
x integer;
y integer;
--定義每個點的可能值,i存放列位置,j存放行位置,val存放可能值(可能值裡面存放的是多個值,根據cur確認當前是那個),cur的值用於從val中找到當前值
TYPE T_POS IS record(
i number,
j number,
val varchar2(10),
cur number);
TYPE T_SEL IS TABLE OF T_POS INDEX BY BINARY_INTEGER;
v_sel T_SEL;
v_n varchar2(10);
v_flag number := 1;
v_sel_count number := 0;
v_hs number := 0;
BEGIN
--列印問題數獨
dbms_output.put_line('問題:');
FOR i IN 1 .. v_init.COUNT LOOP
FOR j IN 1 .. v_init(i).COUNT LOOP
dbms_output.put(v_init(i) (j) || ' ');
END LOOP;
dbms_output.put_line('');
END LOOP;
--透過迴圈找唯一值,把唯一值做為永久值,直到找不到唯一值
while v_flag = 1 loop
v_flag := 0;
v_sel.delete;
FOR i IN 1 .. v_init.COUNT LOOP
FOR j IN 1 .. v_init(i).COUNT LOOP
--v_init(i) (j) = 0的是用來填答案的,其它就是永久值
if v_init(i) (j) = 0 then
v_n := '';
--x,y是用來計算除行,列之後還在去對比的同組中的4個值
x := case
when mod(i, 3) = 0 then
3
else
mod(i, 3)
end;
y := case
when mod(j, 3) = 0 then
3
else
mod(j, 3)
end;
--透過迴圈找到可能值,把可能的值存入v_n
for k in 1 .. 9 loop
if k not in
(--行
v_init(i) (1),
v_init(i) (2),
v_init(i) (3),
v_init(i) (4),
v_init(i) (5),
v_init(i) (6),
v_init(i) (7),
v_init(i) (8),
v_init(i) (9),
--列
v_init(1) (j),
v_init(2) (j),
v_init(3) (j),
v_init(4) (j),
v_init(5) (j),
v_init(6) (j),
v_init(7) (j),
v_init(8) (j),
v_init(9) (j),
--同組另外的四個要對比的值
v_init(i + v_xy(x) (1)) (j + v_xy(y) (1)),
v_init(i + v_xy(x) (1)) (j + v_xy(y) (2)),
v_init(i + v_xy(x) (2)) (j + v_xy(y) (1)),
v_init(i + v_xy(x) (2)) (j + v_xy(y) (2))) then
begin
v_n := v_n || k;
end;
end if;
end loop;
--把可能值的位置,值存入佇列
v_sel_count := v_sel.count + 1;
v_sel(v_sel_count).i := i;
v_sel(v_sel_count).j := j;
v_sel(v_sel_count).val := v_n;
v_sel(v_sel_count).cur := 1;
--如果v_n=1就是唯一值,把這個值永久化,並把v_flag設為1用以再迴圈一次
--如果v_n=0說明在此位置沒有任何合適的值,說明這個數獨開始的資料有問題
if length(v_n) = 1 then
v_init(i)(j) := to_number(v_n);
v_flag := 1;
elsif length(v_n) = 0 then
dbms_output.put_line('數獨初始化的資料錯誤!');
exit;
end if;
end if;
END LOOP;
END LOOP;
end loop;
--把做過唯一值永久化後的資料賦值給答案數獨
v_result := v_init;
--開始回溯演算法透過可能值找最終答案
while v_hs < v_sel.COUNT LOOP
v_hs := v_hs + 1;
--x,y是用來計算除行,列之後還在去對比的同組中的4個值
x := case
when mod(v_sel(v_hs).i, 3) = 0 then
3
else
mod(v_sel(v_hs).i, 3)
end;
y := case
when mod(v_sel(v_hs).j, 3) = 0 then
3
else
mod(v_sel(v_hs).j, 3)
end;
--判斷當前值是否合適,如果不合適,當前位置的可能值向後推進一位,如果當前位置的值已經用完,再後回退,上一層的可能值推進
if to_number(substr(v_sel(v_hs).val, v_sel(v_hs).cur, 1)) in
(v_result(v_sel(v_hs).i) (1),
v_result(v_sel(v_hs).i) (2),
v_result(v_sel(v_hs).i) (3),
v_result(v_sel(v_hs).i) (4),
v_result(v_sel(v_hs).i) (5),
v_result(v_sel(v_hs).i) (6),
v_result(v_sel(v_hs).i) (7),
v_result(v_sel(v_hs).i) (8),
v_result(v_sel(v_hs).i) (9),
v_result(1) (v_sel(v_hs).j),
v_result(2) (v_sel(v_hs).j),
v_result(3) (v_sel(v_hs).j),
v_result(4) (v_sel(v_hs).j),
v_result(5) (v_sel(v_hs).j),
v_result(6) (v_sel(v_hs).j),
v_result(7) (v_sel(v_hs).j),
v_result(8) (v_sel(v_hs).j),
v_result(9) (v_sel(v_hs).j),
v_result(v_sel(v_hs).i + v_xy(x) (1)) (v_sel(v_hs).j + v_xy(y) (1)),
v_result(v_sel(v_hs).i + v_xy(x) (1)) (v_sel(v_hs).j + v_xy(y) (2)),
v_result(v_sel(v_hs).i + v_xy(x) (2)) (v_sel(v_hs).j + v_xy(y) (1)),
v_result(v_sel(v_hs).i + v_xy(x) (2)) (v_sel(v_hs).j + v_xy(y) (2))) then
if v_sel(v_hs).cur < length(v_sel(v_hs).val) then
begin
v_sel(v_hs).cur := v_sel(v_hs).cur + 1;
v_hs := v_hs - 1;
end;
else
if v_hs > 1 then
v_result(v_sel(v_hs).i)(v_sel(v_hs).j) := 0;
v_sel(v_hs).cur := 1;
v_hs := v_hs - 2;
else
dbms_output.put_line('數獨初始化的資料錯誤!');
exit;
end if;
end if;
else
v_result(v_sel(v_hs).i)(v_sel(v_hs).j) := to_number(substr(v_sel(v_hs).val,
v_sel(v_hs).cur,
1));
end if;
END LOOP;
--列印答案數獨
dbms_output.put_line('答案:');
FOR i IN 1 .. v_result.COUNT LOOP
FOR j IN 1 .. v_result(i).COUNT LOOP
dbms_output.put(v_result(i) (j) || ' ');
END LOOP;
dbms_output.put_line('');
END LOOP;
END;
備註:
另外再放兩個數獨,有興趣的可以測試.
(0, 0, 0, 0, 0, 0, 0, 0, 0),
(0, 0, 0, 0, 0, 0, 0, 0, 0),
(0, 0, 0, 0, 0, 0, 0, 0, 0),
(0, 0, 0, 0, 0, 0, 0, 0, 0),
(0, 0, 0, 0, 0, 0, 0, 0, 0),
(0, 0, 0, 0, 0, 0, 0, 0, 0),
(0, 0, 0, 0, 0, 0, 0, 0, 0),
(0, 0, 0, 0, 0, 0, 0, 0, 0),
(0, 0, 0, 0, 0, 0, 0, 0, 0)
---------------------------------------
(0, 6, 2, 0, 0, 0, 5, 0, 0),
(3, 5, 0, 0, 0, 0, 0, 0, 7),
(0, 0, 8, 0, 1, 5, 0, 4, 0),
(0, 0, 0, 0, 6, 0, 9, 0, 8),
(9, 0, 0, 0, 0, 0, 0, 2, 0),
(0, 2, 5, 0, 9, 0, 0, 0, 0),
(5, 0, 0, 0, 3, 0, 7, 0, 0),
(0, 8, 0, 0, 0, 0, 0, 5, 6),
(0, 0, 9, 0, 0, 0, 8, 0, 4)
百度百科最後有提到芬蘭數學家因卡拉花費3個月設計出了世界上迄今難度最大的九宮格遊戲,而且它只有一個答案,就想挑戰一下.水平有限花了2天時間問題解決,速度還行2秒內就
能解答,發現還是比較簡單的,大概的分為兩段,第一段是找到唯一的那些值填上做為永久值,然後再找唯一值做為永久值,直到找不到唯一值.第二段透過回溯,把可能的值按順序嘗試,不
合適就回退,至到找到最終合適的.
以下指令碼及解釋:
DECLARE
--定義數獨行列
TYPE T_ROW IS VARRAY(9) OF NUMBER;
TYPE T_COL_ROW IS VARRAY(9) OF T_ROW;
--初始化數獨
v_init T_COL_ROW := T_COL_ROW(T_ROW(8, 0, 0, 0, 0, 0, 0, 0, 0),
T_ROW(0, 0, 3, 6, 0, 0, 0, 0, 0),
T_ROW(0, 7, 0, 0, 9, 0, 2, 0, 0),
T_ROW(0, 5, 0, 0, 0, 7, 0, 0, 0),
T_ROW(0, 0, 0, 0, 4, 5, 7, 0, 0),
T_ROW(0, 0, 0, 1, 0, 0, 0, 3, 0),
T_ROW(0, 0, 1, 0, 0, 0, 0, 6, 8),
T_ROW(0, 0, 8, 5, 0, 0, 0, 1, 0),
T_ROW(0, 9, 0, 0, 0, 0, 4, 0, 0));
--用以存放答案
v_result T_COL_ROW;
--定義一個用以計算小宮裡面的同組資料的位置
TYPE T_X IS VARRAY(2) OF NUMBER;
TYPE T_Y IS VARRAY(3) OF T_X;
--用以根據相對位置,算出同組資料的絕對位置
v_xy T_Y := T_Y(T_X(1, 2), T_X(-1, 1), T_X(-1, -2));
x integer;
y integer;
--定義每個點的可能值,i存放列位置,j存放行位置,val存放可能值(可能值裡面存放的是多個值,根據cur確認當前是那個),cur的值用於從val中找到當前值
TYPE T_POS IS record(
i number,
j number,
val varchar2(10),
cur number);
TYPE T_SEL IS TABLE OF T_POS INDEX BY BINARY_INTEGER;
v_sel T_SEL;
v_n varchar2(10);
v_flag number := 1;
v_sel_count number := 0;
v_hs number := 0;
BEGIN
--列印問題數獨
dbms_output.put_line('問題:');
FOR i IN 1 .. v_init.COUNT LOOP
FOR j IN 1 .. v_init(i).COUNT LOOP
dbms_output.put(v_init(i) (j) || ' ');
END LOOP;
dbms_output.put_line('');
END LOOP;
--透過迴圈找唯一值,把唯一值做為永久值,直到找不到唯一值
while v_flag = 1 loop
v_flag := 0;
v_sel.delete;
FOR i IN 1 .. v_init.COUNT LOOP
FOR j IN 1 .. v_init(i).COUNT LOOP
--v_init(i) (j) = 0的是用來填答案的,其它就是永久值
if v_init(i) (j) = 0 then
v_n := '';
--x,y是用來計算除行,列之後還在去對比的同組中的4個值
x := case
when mod(i, 3) = 0 then
3
else
mod(i, 3)
end;
y := case
when mod(j, 3) = 0 then
3
else
mod(j, 3)
end;
--透過迴圈找到可能值,把可能的值存入v_n
for k in 1 .. 9 loop
if k not in
(--行
v_init(i) (1),
v_init(i) (2),
v_init(i) (3),
v_init(i) (4),
v_init(i) (5),
v_init(i) (6),
v_init(i) (7),
v_init(i) (8),
v_init(i) (9),
--列
v_init(1) (j),
v_init(2) (j),
v_init(3) (j),
v_init(4) (j),
v_init(5) (j),
v_init(6) (j),
v_init(7) (j),
v_init(8) (j),
v_init(9) (j),
--同組另外的四個要對比的值
v_init(i + v_xy(x) (1)) (j + v_xy(y) (1)),
v_init(i + v_xy(x) (1)) (j + v_xy(y) (2)),
v_init(i + v_xy(x) (2)) (j + v_xy(y) (1)),
v_init(i + v_xy(x) (2)) (j + v_xy(y) (2))) then
begin
v_n := v_n || k;
end;
end if;
end loop;
--把可能值的位置,值存入佇列
v_sel_count := v_sel.count + 1;
v_sel(v_sel_count).i := i;
v_sel(v_sel_count).j := j;
v_sel(v_sel_count).val := v_n;
v_sel(v_sel_count).cur := 1;
--如果v_n=1就是唯一值,把這個值永久化,並把v_flag設為1用以再迴圈一次
--如果v_n=0說明在此位置沒有任何合適的值,說明這個數獨開始的資料有問題
if length(v_n) = 1 then
v_init(i)(j) := to_number(v_n);
v_flag := 1;
elsif length(v_n) = 0 then
dbms_output.put_line('數獨初始化的資料錯誤!');
exit;
end if;
end if;
END LOOP;
END LOOP;
end loop;
--把做過唯一值永久化後的資料賦值給答案數獨
v_result := v_init;
--開始回溯演算法透過可能值找最終答案
while v_hs < v_sel.COUNT LOOP
v_hs := v_hs + 1;
--x,y是用來計算除行,列之後還在去對比的同組中的4個值
x := case
when mod(v_sel(v_hs).i, 3) = 0 then
3
else
mod(v_sel(v_hs).i, 3)
end;
y := case
when mod(v_sel(v_hs).j, 3) = 0 then
3
else
mod(v_sel(v_hs).j, 3)
end;
--判斷當前值是否合適,如果不合適,當前位置的可能值向後推進一位,如果當前位置的值已經用完,再後回退,上一層的可能值推進
if to_number(substr(v_sel(v_hs).val, v_sel(v_hs).cur, 1)) in
(v_result(v_sel(v_hs).i) (1),
v_result(v_sel(v_hs).i) (2),
v_result(v_sel(v_hs).i) (3),
v_result(v_sel(v_hs).i) (4),
v_result(v_sel(v_hs).i) (5),
v_result(v_sel(v_hs).i) (6),
v_result(v_sel(v_hs).i) (7),
v_result(v_sel(v_hs).i) (8),
v_result(v_sel(v_hs).i) (9),
v_result(1) (v_sel(v_hs).j),
v_result(2) (v_sel(v_hs).j),
v_result(3) (v_sel(v_hs).j),
v_result(4) (v_sel(v_hs).j),
v_result(5) (v_sel(v_hs).j),
v_result(6) (v_sel(v_hs).j),
v_result(7) (v_sel(v_hs).j),
v_result(8) (v_sel(v_hs).j),
v_result(9) (v_sel(v_hs).j),
v_result(v_sel(v_hs).i + v_xy(x) (1)) (v_sel(v_hs).j + v_xy(y) (1)),
v_result(v_sel(v_hs).i + v_xy(x) (1)) (v_sel(v_hs).j + v_xy(y) (2)),
v_result(v_sel(v_hs).i + v_xy(x) (2)) (v_sel(v_hs).j + v_xy(y) (1)),
v_result(v_sel(v_hs).i + v_xy(x) (2)) (v_sel(v_hs).j + v_xy(y) (2))) then
if v_sel(v_hs).cur < length(v_sel(v_hs).val) then
begin
v_sel(v_hs).cur := v_sel(v_hs).cur + 1;
v_hs := v_hs - 1;
end;
else
if v_hs > 1 then
v_result(v_sel(v_hs).i)(v_sel(v_hs).j) := 0;
v_sel(v_hs).cur := 1;
v_hs := v_hs - 2;
else
dbms_output.put_line('數獨初始化的資料錯誤!');
exit;
end if;
end if;
else
v_result(v_sel(v_hs).i)(v_sel(v_hs).j) := to_number(substr(v_sel(v_hs).val,
v_sel(v_hs).cur,
1));
end if;
END LOOP;
--列印答案數獨
dbms_output.put_line('答案:');
FOR i IN 1 .. v_result.COUNT LOOP
FOR j IN 1 .. v_result(i).COUNT LOOP
dbms_output.put(v_result(i) (j) || ' ');
END LOOP;
dbms_output.put_line('');
END LOOP;
END;
備註:
另外再放兩個數獨,有興趣的可以測試.
(0, 0, 0, 0, 0, 0, 0, 0, 0),
(0, 0, 0, 0, 0, 0, 0, 0, 0),
(0, 0, 0, 0, 0, 0, 0, 0, 0),
(0, 0, 0, 0, 0, 0, 0, 0, 0),
(0, 0, 0, 0, 0, 0, 0, 0, 0),
(0, 0, 0, 0, 0, 0, 0, 0, 0),
(0, 0, 0, 0, 0, 0, 0, 0, 0),
(0, 0, 0, 0, 0, 0, 0, 0, 0),
(0, 0, 0, 0, 0, 0, 0, 0, 0)
---------------------------------------
(0, 6, 2, 0, 0, 0, 5, 0, 0),
(3, 5, 0, 0, 0, 0, 0, 0, 7),
(0, 0, 8, 0, 1, 5, 0, 4, 0),
(0, 0, 0, 0, 6, 0, 9, 0, 8),
(9, 0, 0, 0, 0, 0, 0, 2, 0),
(0, 2, 5, 0, 9, 0, 0, 0, 0),
(5, 0, 0, 0, 3, 0, 7, 0, 0),
(0, 8, 0, 0, 0, 0, 0, 5, 6),
(0, 0, 9, 0, 0, 0, 8, 0, 4)
來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/28539951/viewspace-1822270/,如需轉載,請註明出處,否則將追究法律責任。
相關文章
- 最難數獨的快速解法 - pythonPython
- PL/SQL程式設計急速上手SQL程式設計
- 【數學】組合數學 - 卡特蘭數
- 困擾數學家25年的“切蘋果”難題,被一位華人統計學博士解決了蘋果
- LearnFast.ai:用AI高效解決數學和物理難題的體驗分享ASTAI
- 解決程式設計難題的實用方法指南 - praeclarum程式設計
- Oralce之PL/SQL程式設計(遊標)SQL程式設計
- Oracle vs PostgreSQL Develop(23) - PL(pg)sql(引數宣告)OracleSQLdev
- 困擾數學家近60年的搬沙發難題疑似被解決!119頁論文證明最優解,百萬網友圍觀
- 數學家竟然藉助神經網路求解世界上最難的方程式?神經網路
- 數百萬惡意簡訊肆虐,芬蘭釋出嚴重警報
- 維數災難:都是孤獨惹的禍
- 怎樣解題|題7.5.12:因數的個數
- [20240607]PL/SQL中sql語句的註解.txtSQL
- SQL 難點解決:序列生成SQL
- 原創:oracle PL/SQL程式設計基礎 上OracleSQL程式設計
- 原創:oracle PL/SQL程式設計基礎 下OracleSQL程式設計
- 機器學習最困難的部分:超引數除錯機器學習除錯
- 程式設計師的數學程式設計師
- 2021數學強化通關330題【數學一-習題冊】 獨家OCR版本
- PL/SQL第一章--概述及變數型別SQL變數型別
- oracle學習筆記(十七) PL/SQL高階應用Oracle筆記SQL
- 6. Oracle開發和應用—6.4. PL/SQL語法—6.4.2. 變數OracleSQL變數
- 數論學習筆記 (3):因數與倍數筆記
- 解決程式(因為數字的問題)沒反應的方法
- 從一個問題中瞭解數學在程式設計中的應用程式設計
- 芬蘭獨立遊戲團隊Redly Games獲Supercell初創投資資金遊戲GAM創投
- PL/SQL 宣告SQL
- Oracle PL/SQLOracleSQL
- 卡特蘭(Catalan)數入門詳解
- 芬蘭的手機遊戲設計底蘊,1人就可以創作出《登山賽車》遊戲設計
- 平面設計難不難學?
- 一道小學數學題的解決
- LeetCode 37. 解數獨LeetCode
- 【原創】視訊+文字:詳解VBA解決數獨問題
- 小學數學程式設計題程式設計
- 程式設計師的數學筆記2--餘數程式設計師筆記
- PL/SQL基本結構---PLSQL複合型別---表型別變數tableSQL型別變數
- 關於一個最簡單的數獨解題實現與疑惑一