Posted in Javascript onOctober 15, 2013
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)当所有的路径搜索下来,答案不是唯一的情况,是和数独游戏的宗旨相悖的。
以下部分是全部代码,为方便阅读,调试信息未删除。
<html> <head> <title>数独解题程序</title> <meta http-equiv="Content-Type" content="text/html; charset=GBK" /> <script> function keygo(evt,obj){ key = window .event?evt.keyCode:evt.which; var next=obj.tabIndex ; var inputs=document.getElementsByTagName("input"); if (key==38){//↑ if (next -9>=0 ) { inputs[next-9].select() } } if (key==40){//↓ if (next +9<81 ) { inputs[next+9].select() } } if (key==37){//← if (next -1>=0 ) { inputs[next-1].select() } } if (key==39){//→ if(next+1<81)inputs[next+1].select(); } } </script> </head> <body onload="init();"> <div id="sodukuTable"></div><input type=button name="cal" onclick="calculate()" value="计算"> <input type=button name="clear" onclick="clearGrid()" value="清空"> <br><span><textarea id="gtxt" cols="19" rows="10">004502006 000000005 002014007 008000012 070080050 930020700 600190200 020000000 300208500</textarea></span> <input type=button name="cal" onclick="paste()" value="粘贴" >可以文本拷贝到下框中后点粘贴,从左到右从上往下的81个数字序列,未填为0,中间非数字字符将忽略<br> <span></span><br><span id="statusDiv"></span><span id="log"></span> <script> var maxRow =9; var maxCol = 9; var strTbody = ["<table id='sodukuTable' border='0'><tbody>"]; for(var i = 0; i < maxRow; i++){ strTbody.push("<tr>"); for(var j = 0; j < maxCol; j++){ strTbody.push("<td style='border-left:"+(j%3==0?1:0) +"px solid black ;border-right:"+(j%3==2?1:0) +"px solid black;border-top:"+(i%3==0?1:0) +"px solid black;border-bottom:"+(i%3==2?1:0)+"px solid black;' ><input style='width:20px;' tabindex='"+(i*9+j) +"'onclick='check(this);' onKeyUp='return keygo(event,this)'type='text' id='input"+(i*9+j)+"'name='n"+(i*9+j)+"'value='"+i+j+"' ></td>"); } strTbody.push("</tr>"); } strTbody.push("</tbody></table>"); var sTbody = ["<table border='1'><tbody>"]; for(var i = 0; i < maxRow; i++){ sTbody.push("<tr>"); for(var j = 0; j < maxCol; j++){ sTbody.push("<td style='border-left:"+(j%3==0?1:0) +"px solid black ;border-right:"+(j%3==2?1:0) +"px solid black;border-top:"+(i%3==0?1:0) +"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>"); } sTbody.push("</tr>"); } sTbody.push("</tbody></table>"); var obj = document.getElementById("sodukuTable"); obj.innerHTML = strTbody.join(""); var obj2 = document.getElementById("statusDiv"); var grid=[ [5, 7, 0, 1, 2, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 6, 7, 0, 0, 0, 8, 0], [3, 0, 4, 0, 0, 9, 0, 7, 0], [0, 2, 0, 0, 7, 0, 0, 5, 0], [0, 1, 0, 3, 0, 0, 9, 0, 2], [0, 8, 0, 0, 0, 2, 1, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 5, 4, 0, 6, 3]]; var candidatNum=[]; var columns=[]; var rows=[]; var blook=[]; var papers=0; var discards=0; var success=false; var steps = new Array(); var log1 = document.getElementById("statusDiv"); function Step(current1,arrys){ this.temp1=new Array(); this.step=[arrys[0],arrys[1],arrys[2]]; for (var i = 0; i < 9; i++) { this.temp1[i]=new Array(); for (var j = 0; j < 9; j++) { this.temp1[i][j]=current1[i][j]; } } } out(grid); init(); function push( current1, i, j, n) { var s = new Step(current1, [ i, j, n ]); steps.push(s); } function pop(){ var step = steps.pop(); discards ++; grid=step.temp1; grid[step.step[0]][step.step[1]] = step.step[2]; var timeline = document.getElementById('PaperList'); timeline.value += ('discard: ['+discards+']:['+papers+']\n'); timeline.scrollTop = timeline.scrollHeight; return step; } function check(obj){ if(obj.value==0)return; for(var i=0;i<9;i++){ for(var j=0;j<9;j++){ var text = document.getElementById("input"+(i*9+j)); if(text.value==obj.value){ text.style.background="green"; }else{ text.style.background=""; } } } } function CheckNumInput(array,num, x, y) { // 目标: // 冲突检查 参数 array:矩阵 num:检测值 x/y:检测位置 // 行列宫均无冲突,return true; // 发现冲突,return false; if (((rows[x] & (1 << num)) == 0) && (columns[y] & (1 << num)) == 0 && (blook[parseInt(x / 3) * 3 + parseInt(y / 3)] & (1 << num)) == 0) { return true; } return false; } function out(array){ var result=true; for (var i = 0; i < 9; i++) { for (var j = 0; j < 9; j++) { document.getElementById("input"+(i*9+j)).value=array[i][j]; if(array[i][j]==0)result=false; } } return result; } function setOne(){ var result = false; //目标: // 遍历矩阵,当前是否可以简单刷新,或者已经可以发现出错. for (var i = 0; i < 9; i++) { for (var j = 0; j < 9; j++) { //目标: // (grid[i][j] == 0 && candidatNum[i][j][0] == 0) >> 没有候选数字,出错了 // (candidatNum[i][j][0] == 1) >> 候选数字唯一 // CheckNumInput(grid,candidatNum[i][j][10],i,j) >> 检查此数字是否符合逻辑 // 判断 没有候选数字||最后一个候选数字不符合逻辑的条件, 从这里回退或者返回出错 if (grid[i][j] == 0 && candidatNum[i][j][0] == 0|| (candidatNum[i][j][0] == 1&&!CheckNumInput(grid,candidatNum[i][j][10],i,j))) { if (grid[i][j] == 0) { result = false; } if (steps.length>0) {// 回退 pop(); // 当前标签已经被证明逻辑错误,废弃 return true; } else { if (!success) { alert("栈为空 结束!"); //题目出错,结束 } return false; } } if (candidatNum[i][j][0] == 1) { //唯一选择 grid[i][j] = candidatNum[i][j][10]; // 更新矩阵 refreshStat3(candidatNum[i][j][10],i,j); // 更新行列宫 candidatNum[i][j][0] = 0; // 标记已选 result = true; continue; } } } if (result == false) { //对于(candidatNum[i][j][0] != 1)的情况,进行筛选 return choose(); } return result; } function refreshStat3( num, x, y) { // 更新行列宫 rows[x] |= 1<<num; columns[y] |= 1<<num; blook[parseInt(x / 3) * 3 + parseInt(y / 3)] |= 1 << num ; } /********************* * 矩阵 数据分析 * 统计 剩余可选项 *********************/ function refreshStat(){ var over = true; // 目标: // 分解行/列/宫 for (var i = 0; i < 9; i++) { rows[i] = 0; //行 columns[i] = 0; //列 blook[i] = 0; //宫 for (var j = 0; j < 9; j++) { if (grid[i][j] != 0) { rows[i] |= 1 << grid[i][j]; } else { rows[i] &= ~(1 << grid[i][j]); } if (grid[j][i] != 0) { columns[i] |= 1 << grid[j][i]; } else { columns[i] &= ~(1 << grid[j][i]); } if (grid[parseInt(i / 3) * 3 + parseInt(j / 3)][i % 3 * 3 + j % 3] != 0) { blook[i] |= 1 << grid[parseInt(i / 3 )* 3 + parseInt(j / 3)][i % 3 * 3 + j % 3]; } } } // 目标: // 遍历矩阵,进行候选标记candidatNum[i][j][0] // candidatNum[i][j][0] = 0; >>>> 已有确定值 // candidatNum[i][j][0] = k; >>>> 可能值数目 // candidatNum[i][j][1] = 987654321x 2进制数位表示的可选数字 for (var i = 0; i < 9; i++) { for (var j = 0; j < 9; j++) { if (grid[i][j] != 0) { candidatNum[i][j][0] = 0; continue; } var size = 0; over = false; for (var k = 1; k < 10; k++) { if (CheckNumInput(grid, k, i, j)) { candidatNum[i][j][1] |= 1 << k; candidatNum[i][j][10] = k; over = false; size++; } else { candidatNum[i][j][1] &= ~(1 << k); } } candidatNum[i][j][0] = size; //标记剩余选项数目 } } return over; } function calculate(){ //功能入口 //读取数据 var start=new Date(); for (var i = 0; i < 9; i++) { for (var j = 0; j < 9; j++) { var text = document.getElementById("input"+(i*9+j)); grid[i][j]=parseInt(text.value); } } //刷新网格 refreshStat(); out(grid); //计算矩阵 while(true){ var a=setOne(); var b=refreshStat(); if(!a||b){ //如果 a==false 或者 b==ture,则可以跳出循环 break; } } out(grid); //答案 alert("用时:"+(new Date()-start)+"ms"); success = true; //计算结束 //验证答案是否唯一 if (papers != discards){ if (steps.length>0) {// 回退 pop(); // 当前标签废弃 //计算矩阵 while(true){ var a=setOne(); var b=refreshStat(); if(!a||b){ //如果 a==false 或者 b==ture,则可以跳出循环 break; } } if (b) { alert("答案不唯一!记录!"); out(grid); //答案 } else { alert("答案唯一!!"); //答案唯一 } } else { alert("出错 结束!"); return false; } } } function clearGrid(){ for (var i = 0; i < 9; i++){ for (var j = 0; j < 9; j++){ grid[i][j]=0; document.getElementById("input"+(i*9+j)).value=grid[i][j]; } } out(grid); } function init(){ for (var i = 0; i < 9; i++) { candidatNum[i]=new Array(); for (var j = 0; j < 9; j++) { candidatNum[i][j]=new Array(); for (var k = 0; k < 11; k++) { candidatNum[i][j][k]=0; } } } } function choose() { //目标: // 遍历矩阵,从当前位置建立搜索分支. var binarynode = false; for (var i = 0; i < 9; i++) { for (var j = 0; j < 9; j++) { // 2叉树分支: // 如果在某位置有两种可能,选项1/选项2 // 则假设是选项1,并进行验算,同时按选项2生成一个新的标签 if (candidatNum[i][j][0] == 2) {// 有2种选择 binarynode = true; var found = -1; for (var k = 1; k < 10; k++) { if ((candidatNum[i][j][1] & (1 << k)) > 0) { if (found > 0) { papers ++; var timeline = document.getElementById('PaperList'); timeline.value += ('add papers:'+papers+':'+i+' '+j+' '+k+'\n'); timeline.scrollTop = timeline.scrollHeight; push(grid, i, j, k); }else{ found = k; } } } grid[i][j] = found; candidatNum[i][j][0] = 0; // 在当前标签上标记已选 var timeline = document.getElementById('PaperList'); timeline.value += ('TRY CURRENT:'+i+' '+j+' '+found+'\n'); timeline.scrollTop = timeline.scrollHeight; return true; } } } if (!binarynode) { var timeline = document.getElementById('PaperList'); timeline.value += ('2叉分支未找到!\n'); timeline.scrollTop = timeline.scrollHeight; for (var i = 0; i < 9; i++) { for (var j = 0; j < 9; j++) { // 2叉树查找失败时,启动3叉树分支,作为扩充,还可以启动3叉树分支: // 如果在某位置有三种可能,选项1/选项2/选项3 // 则假设是选项1,并进行验算,同时按选项2生成一个新的标签 if (candidatNum[i][j][0] == 3) {// 有3种选择 var timeline = document.getElementById('PaperList'); timeline.value += ('发现3叉分支!\n'); timeline.scrollTop = timeline.scrollHeight; binarynode = true; var found = -1; for (var k = 1; k < 10; k++) { if ((candidatNum[i][j][1] & (1 << k)) > 0) { if (found > 0) { papers ++; var timeline = document.getElementById('PaperList'); timeline.value += ('add papers:'+papers+':'+i+' '+j+' '+k+'\n'); timeline.scrollTop = timeline.scrollHeight; push(grid, i, j, k); }else{ found = k; } } } grid[i][j] = found; candidatNum[i][j][0] = 0; // 在当前标签上标记已选 var timeline = document.getElementById('PaperList'); timeline.value += ('TRY CURRENT:'+i+' '+j+' '+found+'\n'); timeline.scrollTop = timeline.scrollHeight; return true; } } } } return false; } function paste(){ var gridstr= document.getElementById("gtxt").value; var a = gridstr.replace(/[^0-9]/g,''); if(a.length!=81){ alert("数据格式不正确:\n"+gridstr); return; } for (var i = 0; i < 9; i++) { for (var j = 0; j < 9; j++){ grid[i][j]=a.charAt(i*9+j); document.getElementById("input"+(i*9+j)).value=grid[i][j]; } } out(grid); papers = 0; discards = 0; success = false; steps = new Array(); } </script> 建议使用IE浏览器,F12开启调试模式<br> <br><span> <textarea id="PaperList" cols="30" rows="20" style="position:absolute;left:550px;"></textarea></span> </body> </html>
Javascript 实现的数独解题算法网页实例
声明:登载此文出于传递更多信息之目的,并不意味着赞同其观点或证实其描述。
Reply on: @reply_date@
@reply_contents@