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 相关文章推荐
javascript 关闭IE6、IE7
Jun 01 Javascript
javascript数组遍历for与for in区别详解
Dec 04 Javascript
微信企业号开发之微信考勤百度地图定位
Sep 11 Javascript
浅析jQuery Ajax通用js封装
Jun 22 Javascript
jQuery 中ajax异步调用的四种方式
Jun 28 Javascript
jQuery实现联动下拉列表查询框
Jan 04 Javascript
bootstrap paginator分页前后台用法示例
Jun 17 Javascript
Vue仿手机qq的实例代码(demo)
Sep 08 Javascript
JavaScript闭包与作用域链实例分析
Jan 21 Javascript
ES6 Proxy实现Vue的变化检测问题
Jun 11 Javascript
React 父子组件通信的实现方法
Dec 05 Javascript
webpack+vue.js构建前端工程化的详细教程
May 10 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源代码
2009/08/21 PHP
php设计模式 Chain Of Responsibility (职责链模式)
2011/06/26 PHP
PHP中使用SimpleXML检查XML文件结构实例
2015/01/07 PHP
php中二维数组排序问题方法详解
2015/08/28 PHP
微信公众平台开发教程⑤ 微信扫码支付模式介绍
2019/04/10 PHP
Laravel创建数据库表结构的例子
2019/10/09 PHP
js实现运行代码需要刷新的解决方法
2007/08/18 Javascript
javascript在IE下trim函数无法使用的解决方法
2014/09/12 Javascript
五种js判断是否为整数类型方式
2015/12/03 Javascript
JavaScript中的原始值和复杂值
2016/01/07 Javascript
深入浅析JavaScript中的Function类型
2016/07/09 Javascript
jQuery简单实现页面元素置顶时悬浮效果示例
2016/08/01 Javascript
详解如何在Angular中快速定位DOM元素
2017/05/17 Javascript
tracking.js页面人脸识别插件使用方法
2020/04/16 Javascript
layui layer select 选择被遮挡的解决方法
2019/09/21 Javascript
jQuery 常用特效实例小结【显示与隐藏、淡入淡出、滑动、动画等】
2020/05/19 jQuery
[40:27]完美世界DOTA2联赛PWL S3 PXG vs GXR 第一场 12.19
2020/12/24 DOTA
python list中append()与extend()用法分享
2013/03/24 Python
使用Node.js和Socket.IO扩展Django的实时处理功能
2015/04/20 Python
Python 26进制计算实现方法
2015/05/28 Python
Python打印斐波拉契数列实例
2015/07/07 Python
Python分析学校四六级过关情况
2017/11/22 Python
PIL对上传到Django的图片进行处理并保存的实例
2019/08/07 Python
python批量修改xml属性的实现方式
2020/03/05 Python
在tensorflow实现直接读取网络的参数(weight and bias)的值
2020/06/24 Python
Python 连接 MySQL 的几种方法
2020/09/09 Python
利用python清除移动硬盘中的临时文件
2020/10/28 Python
用HTML5实现网站在windows8中贴靠的方法
2013/04/21 HTML / CSS
华硕新加坡官方网上商店:ASUS Singapore
2020/07/09 全球购物
linux面试题参考答案(8)
2016/04/19 面试题
秋季婚礼证婚词
2014/01/11 职场文书
新郎父亲婚宴答谢词
2014/01/11 职场文书
销售工作决心书
2015/02/04 职场文书
公司费用报销管理制度
2015/08/04 职场文书
SpringBoot整合JWT的入门指南
2021/06/29 Java/Android
Python 绘制多因子柱状图
2022/05/11 Python