Javascript 实现的数独解题算法

分析由 Javascript 实现的数独页面的算法和实现。

处于对原著的尊敬(代码来自百度文库),所有的分析都写成了注释的形式,这样代码还是继续可以工作的。

本文出自原创,首发www.cnblogs.com/Simba-5830,如转载,请著名出处。

                                                             ------------------- Simba

 

<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 οnlοad="init();">
<div id="sodukuTable"></div><input type=button name="cal" οnclick="calculate()" value="计算">
<input type=button name="clear" οnclick="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" οnclick="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)
		+"'οnclick='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=[	
	[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 candidatNum=[];
   var columns=[];
   var rows=[];
   var blook=[];
   var papers=0;
   var discards=0;
   var temp=[];
   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);
		papers ++;
}
function pop(){
		var step = steps.pop();
		discards ++;
		grid=step.temp1;
		grid[step.step[0]][step.step[1]] = step.step[2];
		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 {
                        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 和 b都为0,则可以跳出循环
	        break;
	    }
	}
	out(grid);    //输出计算结果到显示矩阵
	alert("用时:"+(new Date()-start)+"ms");
	//计算结束
    papers = 0;
    discards = 0;
}
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() {
    //目标:
    //    遍历矩阵,从当前位置建立搜索分支.
    for (var i = 0; i < 9; i++) {
        for (var j = 0; j < 9; j++) {
    //      分支:
    //      如果在某位置有两种可能,选项1/选项2
    //      则假设是选项1,并进行验算,同时按选项2生成一个新的标签
            if (candidatNum[i][j][0] == 2) {// 有2种选择
                var found = -1;
                for (var k = 1; k < 10; k++) {
                    if  ((candidatNum[i][j][1] & (1 << k)) > 0) {
                        if (found > 0) {
                            push(grid, i, j, k);
                        }else{
                            found = k;
                        }
                    }
                }
                grid[i][j] = found;
                candidatNum[i][j][0] = 0; // 在当前标签上标记已选
                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);
}
  </script>
  建议使用IE浏览器,F12开启调试模式
 </body> 
</html>

 

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

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值