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中用toString()方法返回时间为字符串
Jun 12 Javascript
jQuery实现的简洁下拉菜单导航效果代码
Aug 26 Javascript
js实现千分符和保留几位小数的简单实例
Aug 01 Javascript
利用jsonp跨域调用百度js实现搜索框智能提示
Aug 24 Javascript
bootstrap输入框组使用方法
Feb 07 Javascript
js实现从左向右滑动式轮播图效果
Jul 07 Javascript
JavaScript实现简单图片轮播效果
Aug 21 Javascript
BootStrap点击保存后实现模态框自动关闭的思路(模态框)
Sep 26 Javascript
配置eslint规范项目代码风格
Mar 11 Javascript
Vue 实现复制功能,不需要任何结构内容直接复制方式
Nov 09 Javascript
解决vue项目中页面调用数据 在数据加载完毕之前出现undefined问题
Nov 14 Javascript
JavaScript使用canvas绘制随机验证码
Feb 17 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把数字转成人民币大写的函数分享
2014/06/30 PHP
十幅图告诉你什么是PHP引用
2015/02/22 PHP
php实现微信公众平台发红包功能
2018/06/14 PHP
PHP面向对象类型约束用法分析
2019/06/12 PHP
PHP操作Redis常用命令的实例详解
2020/12/23 PHP
javascript-简单的日历实现及Date对象语法介绍(附图)
2013/05/30 Javascript
在javaScript中关于submit和button的区别介绍
2013/10/20 Javascript
jquery浏览器滚动加载技术实现方案
2014/06/03 Javascript
nodejs 实现模拟form表单上传文件
2014/07/14 NodeJs
jquery用ajax方式从后台获取json数据后如何将内容填充到下拉列表
2015/08/26 Javascript
js点击文本框后才加载验证码实例代码
2015/10/20 Javascript
详解JS中的attribute属性
2017/04/25 Javascript
Angular4实现动态添加删除表单输入框功能
2017/08/11 Javascript
ES6学习教程之块级作用域详解
2017/10/09 Javascript
React Native AsyncStorage本地存储工具类
2017/10/24 Javascript
Vue导出json数据到Excel电子表格的示例
2017/12/04 Javascript
详解Require.js与Sea.js的区别
2018/08/05 Javascript
小程序开发踩坑:页面窗口定位(相对于浏览器定位)(推荐)
2019/04/25 Javascript
vue router总结 $router和$route及router与 router与route区别
2019/07/05 Javascript
解决layui页面按钮点击无反应,也不报错的问题
2019/09/29 Javascript
基于ajax实现上传图片代码示例解析
2020/12/03 Javascript
[02:46]解说DC:感谢430陪伴我们的DOTA2国际邀请赛岁月
2016/06/29 DOTA
python getopt 参数处理小示例
2009/06/09 Python
编写Python的web框架中的Model的教程
2015/04/29 Python
Python使用当前时间、随机数产生一个唯一数字的方法
2017/09/18 Python
对Pandas DataFrame缺失值的查找与填充示例讲解
2018/11/06 Python
在pandas中遍历DataFrame行的实现方法
2019/10/23 Python
python中执行smtplib失败的处理方法
2020/07/01 Python
Python爬虫实例——scrapy框架爬取拉勾网招聘信息
2020/07/14 Python
python实现人性化显示金额数字实例详解
2020/09/25 Python
python3实现飞机大战
2020/11/29 Python
HTML5标签与HTML4标签的区别示例介绍
2013/07/18 HTML / CSS
.NET程序员的数据库面试题
2012/10/10 面试题
顶岗实习协议书
2015/01/29 职场文书
海弦WR-800F
2022/04/05 无线电
JavaScript实现简单的音乐播放器
2022/08/14 Javascript