javascript算法解數(shù)獨(dú)實現(xiàn)方案示例
解數(shù)獨(dú)
看《算法的樂趣》,試著用非遞歸窮舉來解數(shù)獨(dú),看效率如何!
數(shù)獨(dú)規(guī)則
數(shù)獨(dú)游戲,經(jīng)典的為9×9=81個單元格組成的九宮格,同時也形成了3×3=9個小九宮格,要求在81個小單元格中填入數(shù)字1~9,并且數(shù)字在每行每列及每個小九宮格中都不能重復(fù)。
數(shù)獨(dú)技巧
- 直觀法
- 候選數(shù)法
- 相關(guān)二十格:一個數(shù)字只與其所在行列及小九宮格的二十格相關(guān)
我的思路
- 精心設(shè)計了有效性判定函數(shù),最多一次遍歷81個小單元格就能做出方案的有效性判定。
- 同理設(shè)計了相關(guān)20格判定,一次0~9的循環(huán)就完成有效性判定。
- 用數(shù)組模擬堆棧,為搜索提供回溯信息。
- 利用對象具有map性質(zhì),來輔助判斷方案的有效性,大大簡化了算法。
方案設(shè)計與實現(xiàn)
只用了一個二維數(shù)組存儲數(shù)獨(dú)方案,一個一維數(shù)組作堆棧,一個布爾變量作回溯標(biāo)識。
變量定義
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對象的哈希特性,為了方便調(diào)試,判定有效時函數(shù)的返回值為0,無效時分三種情況,行沖突、列沖突、小九宮格沖突,分別返回1,2,3。前期判定用了它,后來增加了相關(guān)二十格判定,在找答案時這個函數(shù)就用不上了。
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] //一次內(nèi)循環(huán)同時完成行列的判定 if(row[cur1]) //當(dāng)前元素已經(jīng)在行中出現(xiàn),優(yōu)化掉零的判斷,key為0時值為0,不需要額外判斷 return 1; //返回錯誤代碼 else row[cur1] = cur1 //當(dāng)前元素未在行中出現(xiàn),存入輔助變量中 if(col[cur2]) //列的判定與行類似,優(yōu)化掉零的判斷,key為0時值為0,不需要額外判斷 return 2; else col[cur2] = cur2; let key = Math.floor(i/3)+'-'+Math.floor(j/3) //為不同的小九宮格生成不同的key if(subSudo[key]){ //小九宮格中已經(jīng)有元素,優(yōu)化掉零的判斷,key為0時值為0,不需要額外判斷 if(subSudo[key][cur1]) //對某一個小九宮格的判定與行類似 return 3 else subSudo[key][cur1] = cur1 }else{ //這是某小九宮格中的第一個元素 subSudo[key] = {} //為小九宮格新建一個輔助變量,并將第一個元素存入其中 subSudo[key][cur1] = cur1 } } } return 0; //程序能運(yùn)行到這,說明方案有效 }
相關(guān)二十格判定
原理同整體判定,亮點在小九宮格的定位上。
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){ //當(dāng)前元素已經(jīng)在行中出現(xiàn),優(yōu)化掉零的判斷,key為0時值為0,不需要額外判斷 if(row[cur1]) return 1; //返回錯誤代碼 else row[cur1] = cur1 //當(dāng)前元素未在行中出現(xiàn),存入輔助變量中 } if(cur2){ //列的判定與行類似,優(yōu)化掉零的判斷,key為0時值為0,不需要額外判斷 if(col[cur2]) return 2; else col[cur2] = cur2; } //轉(zhuǎn)化循環(huán)變量到小九宮格的坐標(biāo) let key = sudo[Math.floor(i/3)*3 + Math.floor(k/3)][Math.floor(j/3)*3+Math.floor(k%3)] if(subSudo[key]) //九宮格判定與行類似,優(yōu)化掉零的判斷,key為0時值為0,不需要額外判斷 return 3 else subSudo[key] = key } return 0; }
遍歷求解
利用元素狀態(tài)初值為零的元素即為待定的特性,并加上堆棧的輔助,沒有再開辟額外的存儲空間。
function findAnswer(){ for(let i = 0; i<9; i++){ for(let j = 0; j<9; ){ if(problem[i][j] === 0 || flag){ //當(dāng)前位置為待定元素的首次處理或回溯到當(dāng)前位置,兩種情況看似不同,其實處理相同,自加1即可 flag = false; let k = problem[i][j] + 1; //搜索向下一個合法值邁進(jìn) while(k<10){ //循環(huán)找到下一個合法值 problem[i][j] = k; //填值 if(check20Grid(problem,i,j) == 0){ //判定合法,相關(guān)二十格判定 stack.push([i,j++]) //存儲回溯點,并步進(jìn) break; } k++; } if(k>9){ //當(dāng)前位置找不到合法值,回溯 problem[i][j] = 0; //回溯前歸零 let rt = stack.pop(); //堆棧中取回溯信息 if(!rt) //無解判斷,返回0 return 0; i=rt[0] //穿越 j=rt[1] flag = true; } }else{ //當(dāng)前位置數(shù)字為題目給定 j++; } } } return 1; //成功找到一組解 }
完整代碼
代碼托管在開源中國,其中的sudoku.js即數(shù)獨(dú)解法。
https://git.oschina.net/zhoutk/test.git
答案
書上提到的難度為10.7的題目的答案,1秒內(nèi)解決,效率還行。
[ [ 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 ] ]
以上就是javascript算法解數(shù)獨(dú)實現(xiàn)方案示例的詳細(xì)內(nèi)容,更多關(guān)于javascript算法解數(shù)獨(dú)的資料請關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
Javascript 事件捕獲的備忘(setCapture,captureEvents)
Javascript 事件捕獲的備忘(setCapture,captureEvents)...2006-09-09基于JS+HTML實現(xiàn)彈窗提示是否確認(rèn)提交功能
這篇文章主要介紹了基于JS+HTML實現(xiàn)彈窗提示是否確認(rèn)提交功能,需要的朋友可以參考下2020-06-06js讀取被點擊次數(shù)的簡單實例(從數(shù)據(jù)庫中讀取)
這篇文章主要介紹了js讀取被點擊次數(shù)的簡單實例(從數(shù)據(jù)庫中讀取)。需要的朋友可以過來參考下,希望對大家有所幫助2014-03-03JavaScript中Location.search處理使用方法
本文主要介紹了JavaScript中Location.search處理使用方法,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2023-04-04小程序與內(nèi)嵌webview的數(shù)據(jù)交互方案詳解
這篇文章主要介紹了小程序與內(nèi)嵌webview的數(shù)據(jù)交互方案,為實現(xiàn)H5頁面到小程序的無縫切換,技術(shù)方案包含使用webview交互,特別是低碼C端表單頁面的處理,需要的朋友可以參考下2024-09-09JavaScript function 的 length 屬性使用介紹
函數(shù)的 length 得到的是形參個數(shù),如果函數(shù)內(nèi)部是通過arguments 調(diào)用參數(shù),而沒有實際定義參數(shù)的話, length 只會的得到02014-09-09