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


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 相关文章推荐
用客户端js实现带省略号的分页
Apr 27 Javascript
javascript中数组的sort()方法的使用介绍
Dec 18 Javascript
json的定义、标准格式及json字符串检验
May 11 Javascript
JavaScript中如何通过arguments对象实现对象的重载
May 12 Javascript
七个很有意思的PHP函数
May 12 Javascript
vue实现可增删查改的成绩单
Oct 27 Javascript
javascript如何用递归写一个简单的树形结构示例
Sep 06 Javascript
Vue入门之数据绑定(小结)
Jan 08 Javascript
如何检查一个对象是否为空
Apr 11 Javascript
详解package.json版本号规则
Aug 01 Javascript
JavaScript使用prototype属性实现继承操作示例
May 22 Javascript
Vue实现Header渐隐渐现效果的实例代码
Nov 05 Javascript
高效的获取当前元素是父元素的第几个子元素
Oct 15 #Javascript
JS 实现点击a标签的时候让其背景更换
Oct 15 #Javascript
JS操作Cookies的小例子
Oct 15 #Javascript
如何在父窗口中得知window.open()出的子窗口关闭事件
Oct 15 #Javascript
JavaScript中prototype为对象添加属性的误区介绍
Oct 15 #Javascript
JS格式化数字保留两位小数点示例代码
Oct 15 #Javascript
js计算字符串长度包含的中文是utf8格式
Oct 15 #Javascript
You might like
PHP分页显示制作详细讲解
2006/10/09 PHP
php自动注册登录验证机制实现代码
2011/12/20 PHP
PHP反向代理类代码
2014/08/15 PHP
PHP自带ZIP压缩、解压缩类ZipArchiv使用指南
2015/03/03 PHP
Laravel5.5 实现后台管理登录的方法(自定义用户表登录)
2019/09/30 PHP
javascript 动态调整图片尺寸实现代码
2009/12/28 Javascript
返回对象在当前级别中是第几个元素的实现代码
2011/01/20 Javascript
浅析onsubmit校验表单时利用ajax的return false无效问题
2013/07/10 Javascript
基于Jquery实现键盘按键监听
2014/05/11 Javascript
jquery实现的鼠标下拉滚动置顶效果
2014/07/24 Javascript
Javascript学习笔记之数组的构造函数
2014/11/23 Javascript
JavaScript前补零操作实例
2015/03/11 Javascript
jQuery实现图片文字淡入淡出效果
2015/12/21 Javascript
在JavaScript中模拟类(class)及类的继承关系
2016/05/20 Javascript
详解AngularJS中的表单验证(推荐)
2016/11/17 Javascript
Javascript实现的StopWatch功能示例
2017/06/13 Javascript
[03:16]DOTA2完美大师赛小组赛精彩集锦
2017/11/22 DOTA
python使用wxPython打开并播放wav文件的方法
2015/04/24 Python
Python中字典创建、遍历、添加等实用操作技巧合集
2015/06/02 Python
Python IDLE 错误:IDLE''s subprocess didn''t make connection 的解决方案
2017/02/13 Python
详解Python利用random生成一个列表内的随机数
2019/08/21 Python
Python多线程获取返回值代码实例
2020/02/17 Python
使用Python将Exception异常错误堆栈信息写入日志文件
2020/04/08 Python
解决paramiko执行命令超时的问题
2020/04/16 Python
使用phonegap操作数据库的实现方法
2017/03/31 HTML / CSS
TripAdvisor德国:全球领先的旅游网站
2017/12/07 全球购物
大专生自我鉴定范文
2013/10/01 职场文书
大学生党课思想汇报
2013/12/29 职场文书
旅游管理毕业生自荐信范文
2014/03/19 职场文书
信息工作经验交流材料
2014/05/28 职场文书
关于感恩的演讲稿400字
2014/08/26 职场文书
优秀教研组申报材料
2014/12/26 职场文书
大学生国家助学金感谢信
2015/01/23 职场文书
党员自我评价2015
2015/03/03 职场文书
一个独生女的故事观后感
2015/06/04 职场文书
如何把新闻人物写得立体、鲜活?
2019/08/14 职场文书