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

来源:互联网 发布:it项目管理案例 编辑:程序博客网 时间:2024/05/16 10:36

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">004502006000000005002014007008000012070080050930020700600190200020000000300208500</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>


原创粉丝点击