摘要:數獨技巧直觀法候選數法相關二十格一個數字只與其所在行列及小九宮格的二十格相關我的思路精心設計了有效性判定函數,最多一次遍歷個小單元格就能做出方案的有效性判定。
看《算法的樂趣》,試著用非遞歸窮舉來解數獨,看效率如何!
數獨規則數獨游戲,經典的為9×9=81個單元格組成的九宮格,同時也形成了3×3=9個小九宮格,要求在81個小單元格中填入數字1~9,并且數字在每行每列及每個小九宮格中都不能重復。
數獨技巧直觀法
候選數法
相關二十格:一個數字只與其所在行列及小九宮格的二十格相關
我的思路精心設計了有效性判定函數,最多一次遍歷81個小單元格就能做出方案的有效性判定。
同理設計了相關20格判定,一次0~9的循環就完成有效性判定。
用數組模擬堆棧,為搜索提供回溯信息。
利用對象具有map性質,來輔助判斷方案的有效性,大大簡化了算法。
方案設計與實現只用了一個二維數組存儲數獨方案,一個一維數組作堆棧,一個布爾變量作回溯標識。
變量定義var problem = [ //這是書上提到的難度10.7的題 [8,0,0,0,0,0,0,0,0], [0,0,3,6,0,0,0,0,0], [0,7,0,0,9,0,2,0,0], [0,5,0,0,0,7,0,0,0], [0,0,0,0,4,5,7,0,0], [0,0,0,1,0,0,0,3,0], [0,0,1,0,0,0,0,6,8], [0,0,8,5,0,0,0,1,0], [0,9,0,0,0,0,4,0,0] ] var stack = [],flag = false;方案有效性判定
充分利用了javascript對象的哈希特性,為了方便調試,判定有效時函數的返回值為0,無效時分三種情況,行沖突、列沖突、小九宮格沖突,分別返回1,2,3。前期判定用了它,后來增加了相關二十格判定,在找答案時這個函數就用不上了。
function checkValid(sudo){ let subSudo = {} //輔助變量,用來判定小九宮格是否沖突 for(let i = 0; i<9; i++){ let row = {}, col = {} //輔助變量,用來判定行、列是否沖突 for(let j = 0; j<9; j++){ let cur1 = sudo[i][j], cur2 = sudo[j][i] //一次內循環同時完成行列的判定 if(row[cur1]) //當前元素已經在行中出現,優化掉零的判斷,key為0時值為0,不需要額外判斷 return 1; //返回錯誤代碼 else row[cur1] = cur1 //當前元素未在行中出現,存入輔助變量中 if(col[cur2]) //列的判定與行類似,優化掉零的判斷,key為0時值為0,不需要額外判斷 return 2; else col[cur2] = cur2; let key = Math.floor(i/3)+"-"+Math.floor(j/3) //為不同的小九宮格生成不同的key if(subSudo[key]){ //小九宮格中已經有元素,優化掉零的判斷,key為0時值為0,不需要額外判斷 if(subSudo[key][cur1]) //對某一個小九宮格的判定與行類似 return 3 else subSudo[key][cur1] = cur1 }else{ //這是某小九宮格中的第一個元素 subSudo[key] = {} //為小九宮格新建一個輔助變量,并將第一個元素存入其中 subSudo[key][cur1] = cur1 } } } return 0; //程序能運行到這,說明方案有效 }相關二十格判定
原理同整體判定,亮點在小九宮格的定位上。
function check20Grid(sudo,i,j){ let row = {}, col = {}, subSudo = {} //輔助變量 for(let k = 0; k < 9; k++){ let cur1 = sudo[i][k], cur2 = sudo[k][j] if(cur1){ //當前元素已經在行中出現,優化掉零的判斷,key為0時值為0,不需要額外判斷 if(row[cur1]) return 1; //返回錯誤代碼 else row[cur1] = cur1 //當前元素未在行中出現,存入輔助變量中 } if(cur2){ //列的判定與行類似,優化掉零的判斷,key為0時值為0,不需要額外判斷 if(col[cur2]) return 2; else col[cur2] = cur2; } //轉化循環變量到小九宮格的坐標 let key = sudo[Math.floor(i/3)*3 + Math.floor(k/3)][Math.floor(j/3)*3+Math.floor(k%3)] if(subSudo[key]) //九宮格判定與行類似,優化掉零的判斷,key為0時值為0,不需要額外判斷 return 3 else subSudo[key] = key } return 0; }遍歷求解
利用元素狀態初值為零的元素即為待定的特性,并加上堆棧的輔助,沒有再開辟額外的存儲空間。
function findAnswer(){ for(let i = 0; i<9; i++){ for(let j = 0; j<9; ){ if(problem[i][j] === 0 || flag){ //當前位置為待定元素的首次處理或回溯到當前位置,兩種情況看似不同,其實處理相同,自加1即可 flag = false; let k = problem[i][j] + 1; //搜索向下一個合法值邁進 while(k<10){ //循環找到下一個合法值 problem[i][j] = k; //填值 if(check20Grid(problem,i,j) == 0){ //判定合法,相關二十格判定 stack.push([i,j++]) //存儲回溯點,并步進 break; } k++; } if(k>9){ //當前位置找不到合法值,回溯 problem[i][j] = 0; //回溯前歸零 let rt = stack.pop(); //堆棧中取回溯信息 if(!rt) //無解判斷,返回0 return 0; i=rt[0] //穿越 j=rt[1] flag = true; } }else{ //當前位置數字為題目給定 j++; } } } return 1; //成功找到一組解 }完整代碼
代碼托管在開源中國,其中的sudoku.js即數獨解法。
https://git.oschina.net/zhoutk/test.git答案
書上提到的難度為10.7的題目的答案,1秒內解決,效率還行。
[ [ 8, 1, 2, 7, 5, 3, 6, 4, 9 ], [ 9, 4, 3, 6, 8, 2, 1, 7, 5 ], [ 6, 7, 5, 4, 9, 1, 2, 8, 3 ], [ 1, 5, 4, 2, 3, 7, 8, 9, 6 ], [ 3, 6, 9, 8, 4, 5, 7, 2, 1 ], [ 2, 8, 7, 1, 6, 9, 5, 3, 4 ], [ 5, 2, 1, 9, 7, 4, 3, 6, 8 ], [ 4, 3, 8, 5, 2, 6, 9, 1, 7 ], [ 7, 9, 6, 3, 1, 8, 4, 5, 2 ] ]
文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。
轉載請注明本文地址:http://specialneedsforspecialkids.com/yun/79284.html
摘要:首先在二維數組第一行隨機填充個數字,然后將這個數字隨機分布到整個二維數組中,然后使用求解數獨的算法對此時的數組進行求解,得到一個完整的數獨,然后按照用戶輸入的提示數量進行隨機挖洞,得到最終的數獨題目。 思路 1.生成數獨 數獨的生成總體思路是挖洞法。首先在二維數組第一行隨機填充1-9 9個數字,然后將這9個數字隨機分布到整個二維數組中,然后使用求解數獨的算法對此時的數組進行求解,得到一...
摘要:可以針對筆者常用的數獨本文的實現都基于該,實現數獨的識別求解并把答案自動填入。專家級別的平均秒完成求解包括圖像數字提取,識別過程,完成全部操作。步驟四數獨求解,生成答案,并生成需要填充的數字序列。 1 序 ??數獨是源自18世紀瑞士的一種數學游戲。是一種運用紙、筆進行演算的邏輯游戲。玩家需要根據9×9盤面上的已知數字,推理出所有剩余空格的數字,并滿足每一行、每一列、每一個粗線宮(3*3...
摘要:而此處針對進一步的搜索,有兩個問題需要考慮如何選取搜索起點方格確定哪種搜索策略深度優先搜索,廣度優先搜索關于第一個問題,無論選擇哪個方格起始搜索,對于能否解決問題來說并不存在差異。 Github倉庫地址 學習是為了尋找解決問題的答案,若脫離了問題只為知曉而進行的打call,那么隨時間流逝所沉淀下來的,估計就只有重在參與的虛幻存在感了,自學的人就更應善于發現可供解決的問題。為了入門AI,...
摘要:上圖是一個部分填充的有效的數獨。數獨部分空格內已填入了數字,空白格用表示。但由于位于左上角的宮內有兩個存在因此這個數獨是無效的。說明一個有效的數獨部分已被填充不一定是可解的。只需要根據以上規則,驗證已經填入的數字是否有效即可。 判斷一個 9x9的數獨是否有效。只需要根據以下規則,驗證已經填入的數字是否有效即可。 數字 1-9 在每一行只能出現一次。 數字 1-9 在每一列只能出現一次...
閱讀 1893·2021-11-24 11:16
閱讀 3256·2021-09-10 10:51
閱讀 3179·2021-08-03 14:03
閱讀 1261·2019-08-29 17:03
閱讀 3238·2019-08-29 12:36
閱讀 2218·2019-08-26 14:06
閱讀 492·2019-08-23 16:32
閱讀 2660·2019-08-23 13:42