Javascript 实现的数独解题算法网页实例
Javascript 实现的数独解题算法网页实例
发布时间:2016-12-30 来源:查字典编辑
摘要:1)当我们拿到一个题目时,首先会根据已经知道的条件,进行数据的初步整理和分析。相当于填写出9宫格里,所有的“确定项”,以及标记“可能选项”。...

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" value="计算">

<input type=button name="clear" 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" 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+(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 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+(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 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"></textarea></span>

</body>

</html>

推荐文章
猜你喜欢
附近的人在看
推荐阅读
拓展阅读
相关阅读
网友关注
最新Javascript教程学习
热门Javascript教程学习
编程开发子分类