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显示当前文档最后修改日期的方法
Mar 19 Javascript
JavaScript小技巧整理篇(非常全)
Jan 26 Javascript
JS实现iframe编辑器光标位置插入内容的方法(兼容IE和Firefox)
Jun 24 Javascript
深入理解Node中的buffer模块
Jun 03 Javascript
node实现简单的反向代理服务器
Jul 26 Javascript
浏览器调试动态js脚本的方法(图解)
Jan 19 Javascript
详解使用create-react-app添加css modules、sasss和antd
Jul 31 Javascript
d3绘制基本的柱形图的实现代码
Dec 12 Javascript
使用vue制作滑动标签
Sep 21 Javascript
vue 中的 render 函数作用详解
Feb 28 Javascript
JS时间戳与日期格式互相转换的简单方法示例
Jan 30 Javascript
如何在vue中使用HTML 5 拖放API
Jan 14 Vue.js
高效的获取当前元素是父元素的第几个子元素
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
通过curl模拟post和get方式提交的表单类
2014/04/23 PHP
从性能方面考虑PHP下载远程文件的3种方法
2015/12/29 PHP
web 页面分页打印的实现
2009/06/22 Javascript
Prototype 学习 工具函数学习($w,$F方法)
2009/07/12 Javascript
Pro JavaScript Techniques学习笔记
2010/12/28 Javascript
onclick与listeners的执行先后问题详细解剖
2013/01/07 Javascript
js实现可拖动DIV的方法
2013/12/17 Javascript
Jqgrid设置全选(选择)及获取选择行的值示例代码
2013/12/28 Javascript
JQuery EasyUI 数字格式化处理示例
2014/05/05 Javascript
JavaScript控制网页平滑滚动到指定元素位置的方法
2015/04/17 Javascript
jfinal与bootstrap的登录跳转实战演习
2015/09/22 Javascript
Javascript中的数组常用方法解析
2016/06/17 Javascript
easyui form validate总是返回false的原因及解决方法
2016/11/07 Javascript
javascript自执行函数
2017/02/10 Javascript
js精确的加减乘除实例
2017/11/14 Javascript
JS中常用的消息框总结
2018/02/24 Javascript
JS/HTML5游戏常用算法之追踪算法实例详解
2018/12/12 Javascript
微信小程序 setData 对 data数据影响问题
2019/04/18 Javascript
IE11下CKEditor在Bootstrap Modal中下拉问题的解决
2019/09/25 Javascript
[30:37]【全国守擂赛】第三周擂主赛 Dark Knight vs. Leopard Gaming
2020/05/04 DOTA
Python中的条件判断语句与循环语句用法小结
2016/03/21 Python
python用pickle模块实现“增删改查”的简易功能
2017/06/07 Python
Python温度转换实例分析
2018/01/17 Python
python实现m3u8格式转换为mp4视频格式
2018/02/28 Python
Python延时操作实现方法示例
2018/08/14 Python
Django 全局的static和templates的使用详解
2019/07/19 Python
用python解压分析jar包实例
2020/01/16 Python
CSS实现半透明边框与多重边框的场景分析
2019/11/13 HTML / CSS
如何用Lucene索引数据库
2016/02/23 面试题
LINUX下线程,GDI类的解释
2012/04/17 面试题
《乌鸦和狐狸》教学反思
2014/02/08 职场文书
艺校音乐专业自我鉴定范文
2014/03/01 职场文书
科研课题实施方案
2014/03/18 职场文书
2016大学军训心得体会
2016/01/11 职场文书
JavaScript高级程序设计之变量与作用域
2021/11/17 Javascript