Javascript 实现的数独解题算法网页

此算法的实现,就是模拟人脑的思考和计算过程。

————————————————————————————-————————

1)

当我们拿到一个题目时,首先会根据已经知道的条件,进行数据的初步整理和分析。

相当于填写出9宫格里,所有的“确定项”,以及标记“可能选项”。

function refreshStat()

2)

此后,思考会进入 猜测/验证 的循环阶段。

在9宫格中,可以对于“可能选项”进行尝试,验证是否违背现有条件。

每一个新的分支,最后的结果无非是两种,答案/出错。

                while(true){
                    var a=setOne();
                    var b=refreshStat();
                    if(!a||b){ //如果 a==false 或者 b==ture,则可以跳出循环
                        break;
                    }
                }

实际人脑思考的过程,也是要先遍历选项较少的分支。

所以,程序实现上也是 确定点/2叉分支/3叉分支/....

3)

当所有的路径搜索下来,答案不是唯一的情况,是和数独游戏的宗旨相悖的。

————————————————————————————-————————

以下部分是全部代码,为方便阅读,调试信息未删除。

————————————————————————————-————————

  1 <html>
  2  <head>
  3   <title>数独解题程序</title>
  4 <meta http-equiv="Content-Type" content="text/html; charset=GBK" />
  5   <script>
  6    function keygo(evt,obj){
  7        key = window .event?evt.keyCode:evt.which;
  8        var next=obj.tabIndex ;
  9        var inputs=document.getElementsByTagName("input");
 10        if (key==38){//
 11            if (next -9>=0 ) {
 12                inputs[next-9].select()
 13            }
 14        }
 15        if (key==40){//
 16            if (next +9<81 ) {
 17                inputs[next+9].select()
 18            }
 19        }
 20        if (key==37){//
 21            if (next -1>=0 ) {
 22                inputs[next-1].select()
 23            }
 24        }
 25        if (key==39){//
 26            if(next+1<81)inputs[next+1].select();
 27        }
 28    }
 29   </script>
 30  </head>
 31 <body onload="init();">
 32 <div id="sodukuTable"></div><input type=button name="cal" onclick="calculate()" value="计算">
 33 <input type=button name="clear" onclick="clearGrid()" value="清空">
 34 <br><span><textarea id="gtxt" cols="19" rows="10">004502006
 35 000000005
 36 002014007
 37 008000012
 38 070080050
 39 930020700
 40 600190200
 41 020000000
 42 300208500</textarea></span>
 43 <input type=button name="cal" onclick="paste()" value="粘贴" >可以文本拷贝到下框中后点粘贴,从左到右从上往下的81个数字序列,未填为0,中间非数字字符将忽略<br>
 44 <span></span><br><span id="statusDiv"></span><span id="log"></span>
 45   <script>
 46    var maxRow =9;
 47    var maxCol = 9;
 48    var strTbody = ["<table id='sodukuTable' border='0'><tbody>"];
 49    for(var i = 0; i < maxRow; i++){
 50     strTbody.push("<tr>");
 51      for(var j = 0; j < maxCol; j++){
 52       strTbody.push("<td style='border-left:"+(j%3==0?1:0)
 53             +"px solid black ;border-right:"+(j%3==2?1:0)
 54             +"px solid black;border-top:"+(i%3==0?1:0)
 55             +"px solid black;border-bottom:"+(i%3==2?1:0)+"px solid black;' ><input style='width:20px;' tabindex='"+(i*9+j)
 56         +"'οnclick='check(this);' onKeyUp='return keygo(event,this)'type='text' id='input"+(i*9+j)+"'name='n"+(i*9+j)+"'value='"+i+j+"' ></td>");
 57      }
 58     strTbody.push("</tr>"); 
 59    }
 60    strTbody.push("</tbody></table>"); 
 61    var sTbody = ["<table border='1'><tbody>"];
 62    for(var i = 0; i < maxRow; i++){
 63     sTbody.push("<tr>");
 64      for(var j = 0; j < maxCol; j++){
 65       sTbody.push("<td style='border-left:"+(j%3==0?1:0)
 66             +"px solid black ;border-right:"+(j%3==2?1:0)
 67             +"px solid black;border-top:"+(i%3==0?1:0)
 68             +"px solid black;border-bottom:"+(i%3==2?1:0)+"px solid black;'><div style='width:25px;height:25px'  id='status"+(i*9+j)+"'name='div"+i+j+"'  ></div></td>");
 69      }
 70     sTbody.push("</tr>"); 
 71    }
 72    sTbody.push("</tbody></table>"); 
 73    var obj = document.getElementById("sodukuTable");
 74    obj.innerHTML = strTbody.join("");
 75    var obj2 = document.getElementById("statusDiv");
 76    var grid=[    
 77     [5, 7, 0, 1, 2, 0, 0, 0, 0],
 78     [0, 0, 0, 0, 0, 0, 0, 0, 0],
 79     [0, 0, 6, 7, 0, 0, 0, 8, 0],
 80     [3, 0, 4, 0, 0, 9, 0, 7, 0],
 81     [0, 2, 0, 0, 7, 0, 0, 5, 0],
 82     [0, 1, 0, 3, 0, 0, 9, 0, 2],
 83     [0, 8, 0, 0, 0, 2, 1, 0, 0],
 84     [0, 0, 0, 0, 0, 0, 0, 0, 0],
 85     [0, 0, 0, 0, 5, 4, 0, 6, 3]];
 86    var candidatNum=[];
 87    var columns=[];
 88    var rows=[];
 89    var blook=[];
 90    var papers=0;
 91    var discards=0;
 92    var success=false;
 93    var steps = new Array();
 94    var log1 = document.getElementById("statusDiv");
 95 
 96 function Step(current1,arrys){
 97         this.temp1=new Array();
 98         this.step=[arrys[0],arrys[1],arrys[2]];
 99         for (var i = 0; i < 9; i++)
100             {
101                 this.temp1[i]=new Array();
102                 for (var j = 0; j < 9; j++)
103                     {
104                     this.temp1[i][j]=current1[i][j];
105                     }
106                 }
107 }
108 out(grid);
109 init();
110 
111 function push( current1,  i,  j,  n) {
112         var s = new Step(current1, [ i, j, n ]);
113         steps.push(s);
114 }
115 function pop(){
116         var step = steps.pop();
117         discards ++;
118         grid=step.temp1;
119         grid[step.step[0]][step.step[1]] = step.step[2];
120                 var timeline = document.getElementById('PaperList');
121                 timeline.value += ('discard: ['+discards+']:['+papers+']\n');
122                 timeline.scrollTop = timeline.scrollHeight;
123         return step;
124 }
125     
126 function check(obj){
127     if(obj.value==0)return;
128     for(var i=0;i<9;i++){
129         for(var j=0;j<9;j++){
130             var text = document.getElementById("input"+(i*9+j));
131             if(text.value==obj.value){
132                 text.style.background="green";
133             }else{
134                 text.style.background="";
135             }
136         }
137 
138     }
139 
140 }
141 function CheckNumInput(array,num,  x,  y)  {
142     //  目标:
143     //      冲突检查  参数 array:矩阵 num:检测值 x/y:检测位置
144     //      行列宫均无冲突,return true;
145     //            发现冲突,return false;
146     if (((rows[x] & (1 << num)) == 0) && (columns[y] & (1 << num)) == 0
147         && (blook[parseInt(x / 3) * 3 + parseInt(y / 3)] & (1 << num)) == 0) {
148         return true;
149         }
150     return false;
151 }
152 
153 function out(array){
154     var result=true;
155     for (var i = 0; i < 9; i++)
156         {
157             for (var j = 0; j < 9; j++)
158             {
159         document.getElementById("input"+(i*9+j)).value=array[i][j];
160                 if(array[i][j]==0)result=false;
161             }
162         }
163         return result;
164 }
165 function setOne(){
166     var result = false;
167     //目标:
168     //    遍历矩阵,当前是否可以简单刷新,或者已经可以发现出错.
169     for (var i = 0; i < 9; i++) {
170         for (var j = 0; j < 9; j++) {
171             //目标:
172             // (grid[i][j] == 0 && candidatNum[i][j][0] == 0)  >> 没有候选数字,出错了
173             // (candidatNum[i][j][0] == 1)                     >> 候选数字唯一
174             // CheckNumInput(grid,candidatNum[i][j][10],i,j)   >> 检查此数字是否符合逻辑
175             // 判断 没有候选数字||最后一个候选数字不符合逻辑的条件,  从这里回退或者返回出错
176             if (grid[i][j] == 0 && candidatNum[i][j][0] == 0||
177                (candidatNum[i][j][0] == 1&&!CheckNumInput(grid,candidatNum[i][j][10],i,j))) {
178                     if (grid[i][j] == 0) {
179                        result = false;
180                     }
181                     if (steps.length>0) {// 回退
182                         pop();           // 当前标签已经被证明逻辑错误,废弃
183                         return true;
184                     } else {
185                         if (!success) {
186                             alert("栈为空 结束!");    //题目出错,结束
187                             }
188                         return false;
189                     }
190             }
191             if (candidatNum[i][j][0] == 1) {              //唯一选择
192                 grid[i][j] = candidatNum[i][j][10];       //  更新矩阵
193                 refreshStat3(candidatNum[i][j][10],i,j);  //  更新行列宫
194                 candidatNum[i][j][0] = 0;                 //  标记已选
195                 result = true;
196                 continue;
197             }
198         
199         }
200     }
201     if (result == false) { //对于(candidatNum[i][j][0] != 1)的情况,进行筛选
202         return choose();
203     }
204     return result;
205 }
206 function refreshStat3( num, x, y) {   //  更新行列宫
207         rows[x] |= 1<<num;
208         columns[y] |= 1<<num;
209         blook[parseInt(x / 3) * 3 + parseInt(y / 3)] |= 1 << num ;
210 
211 }
212 /*********************
213  * 矩阵 数据分析
214  * 统计 剩余可选项
215  *********************/
216 function refreshStat(){
217     var over = true;
218     //  目标:
219     //      分解行/列/宫
220     for (var i = 0; i < 9; i++) {
221         rows[i] = 0;    //
222         columns[i] = 0; //
223         blook[i] = 0;   //
224         for (var j = 0; j < 9; j++) {
225             if (grid[i][j] != 0) {
226                 rows[i] |= 1 << grid[i][j];
227             } else {
228                 rows[i] &= ~(1 << grid[i][j]);
229             }
230             if (grid[j][i] != 0) {
231                 columns[i] |= 1 << grid[j][i];
232             } else {
233                 columns[i] &= ~(1 << grid[j][i]);
234             }
235             if (grid[parseInt(i / 3) * 3 + parseInt(j / 3)][i % 3 * 3 + j % 3] != 0) {
236                 blook[i] |= 1 << grid[parseInt(i / 3 )* 3 + parseInt(j / 3)][i % 3 * 3 + j % 3];
237             }
238         }
239     }
240     //  目标:
241     //      遍历矩阵,进行候选标记candidatNum[i][j][0]
242     //      candidatNum[i][j][0] = 0;    >>>>    已有确定值
243     //      candidatNum[i][j][0] = k;    >>>>    可能值数目
244     //      candidatNum[i][j][1] = 987654321x    2进制数位表示的可选数字
245     for (var i = 0; i < 9; i++) {
246         for (var j = 0; j < 9; j++) {
247         if (grid[i][j] != 0) {
248             candidatNum[i][j][0] = 0;
249             continue;
250         }
251         var size = 0;
252         over = false;
253         for (var k = 1; k < 10; k++) {
254             if (CheckNumInput(grid, k, i, j)) {
255                 candidatNum[i][j][1] |= 1 << k;
256                 candidatNum[i][j][10] = k;
257                 over = false;
258                 size++;
259             } else {
260                 candidatNum[i][j][1] &= ~(1 << k);
261             }
262         }
263         candidatNum[i][j][0] = size;  //标记剩余选项数目
264     }
265     }
266     return over;
267 }
268 
269 function calculate(){  //功能入口
270     //读取数据
271     var start=new Date();
272     for (var i = 0; i < 9; i++)
273         {    
274             for (var j = 0; j < 9; j++)
275             {
276                 var text = document.getElementById("input"+(i*9+j));
277                 grid[i][j]=parseInt(text.value);
278         }
279     }
280     
281     //刷新网格    
282     refreshStat();
283     out(grid);
284     //计算矩阵
285     while(true){
286         var a=setOne();
287         var b=refreshStat();
288         if(!a||b){ //如果 a==false 或者 b==ture,则可以跳出循环
289             break;
290         }
291     }
292     out(grid);    //答案
293     alert("用时:"+(new Date()-start)+"ms");
294     success = true;
295     //计算结束
296     
297     //验证答案是否唯一
298     if (papers != discards){
299             if (steps.length>0) {// 回退
300                 pop();           // 当前标签废弃
301                 //计算矩阵
302                 while(true){
303                     var a=setOne();
304                     var b=refreshStat();
305                     if(!a||b){ //如果 a==false 或者 b==ture,则可以跳出循环
306                         break;
307                     }
308                 }
309                 if (b) {
310                     alert("答案不唯一!记录!");
311                     out(grid);    //答案
312                     }
313                 else {
314                     alert("答案唯一!!");    //答案唯一
315                 }
316             } else {
317                 alert("出错 结束!");
318                 return false;
319             }
320     }
321 }
322 function clearGrid(){
323     for (var i = 0; i < 9; i++){
324         for (var j = 0; j < 9; j++){
325             grid[i][j]=0;
326             document.getElementById("input"+(i*9+j)).value=grid[i][j];
327         }
328     }
329     out(grid);
330 }
331 function init(){
332      for (var i = 0; i < 9; i++)
333         {    candidatNum[i]=new Array();
334             
335             for (var j = 0; j < 9; j++)
336             {    candidatNum[i][j]=new Array();
337 
338         for (var k = 0; k < 11; k++)
339                 {    candidatNum[i][j][k]=0;
340         }
341         }
342     }
343 }
344 function choose() {
345     //目标:
346     //    遍历矩阵,从当前位置建立搜索分支.
347     var binarynode = false;
348     for (var i = 0; i < 9; i++) {
349         for (var j = 0; j < 9; j++) {
350     //      2叉树分支:
351     //      如果在某位置有两种可能,选项1/选项2
352     //      则假设是选项1,并进行验算,同时按选项2生成一个新的标签
353             if (candidatNum[i][j][0] == 2) {// 有2种选择
354                 binarynode = true;
355                 var found = -1;
356                 for (var k = 1; k < 10; k++) {
357                     if  ((candidatNum[i][j][1] & (1 << k)) > 0) {
358                         if (found > 0) {
359                             papers ++;
360                             var timeline = document.getElementById('PaperList');
361                             timeline.value += ('add papers:'+papers+':'+i+' '+j+' '+k+'\n');
362                             timeline.scrollTop = timeline.scrollHeight;
363                             push(grid, i, j, k);
364                         }else{
365                             found = k;
366                         }
367                     }
368                 }
369                 grid[i][j] = found;
370                 candidatNum[i][j][0] = 0; // 在当前标签上标记已选
371                             var timeline = document.getElementById('PaperList');
372                             timeline.value += ('TRY CURRENT:'+i+' '+j+' '+found+'\n');
373                             timeline.scrollTop = timeline.scrollHeight;
374                 return true;
375             }
376         }
377     }
378     if (!binarynode) {
379     var timeline = document.getElementById('PaperList');
380     timeline.value += ('2叉分支未找到!\n');
381     timeline.scrollTop = timeline.scrollHeight;
382         for (var i = 0; i < 9; i++) {
383             for (var j = 0; j < 9; j++) {
384         // 2叉树查找失败时,启动3叉树分支,作为扩充,还可以启动3叉树分支:
385         //      如果在某位置有三种可能,选项1/选项2/选项3
386         //      则假设是选项1,并进行验算,同时按选项2生成一个新的标签
387                 if (candidatNum[i][j][0] == 3) {// 有3种选择
388                             var timeline = document.getElementById('PaperList');
389                             timeline.value += ('发现3叉分支!\n');
390                             timeline.scrollTop = timeline.scrollHeight;
391                     binarynode = true;
392                     var found = -1;
393                     for (var k = 1; k < 10; k++) {
394                         if  ((candidatNum[i][j][1] & (1 << k)) > 0) {
395                             if (found > 0) {
396                                 papers ++;
397                             var timeline = document.getElementById('PaperList');
398                             timeline.value += ('add papers:'+papers+':'+i+' '+j+' '+k+'\n');
399                             timeline.scrollTop = timeline.scrollHeight;
400                                 push(grid, i, j, k);
401                             }else{
402                                 found = k;
403                             }
404                         }
405                     }
406                     grid[i][j] = found;
407                     candidatNum[i][j][0] = 0; // 在当前标签上标记已选
408                             var timeline = document.getElementById('PaperList');
409                             timeline.value += ('TRY CURRENT:'+i+' '+j+' '+found+'\n');
410                             timeline.scrollTop = timeline.scrollHeight;
411                     return true;
412                 }
413             }
414         }
415     }
416     return false;
417 }
418 function paste(){
419     var gridstr= document.getElementById("gtxt").value;
420     var a = gridstr.replace(/[^0-9]/g,'');
421     if(a.length!=81){
422        alert("数据格式不正确:\n"+gridstr);
423        return;
424     }
425     for (var i = 0; i < 9; i++)
426         {
427             for (var j = 0; j < 9; j++){
428                 grid[i][j]=a.charAt(i*9+j);
429                 document.getElementById("input"+(i*9+j)).value=grid[i][j];
430         }
431     }
432     out(grid);
433     papers = 0;
434     discards = 0;
435     success = false;
436     steps = new Array();
437 }
438   </script>
439   建议使用IE浏览器,F12开启调试模式<br>
440 <br><span>
441 <textarea id="PaperList" cols="30" rows="20" style="position:absolute;left:550px;"></textarea></span>
442  </body> 
443 </html>

 

转载于:https://www.cnblogs.com/Simba-5830/p/3370455.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值