视频1 视频21 视频41 视频61 视频文章1 视频文章21 视频文章41 视频文章61 推荐1 推荐3 推荐5 推荐7 推荐9 推荐11 推荐13 推荐15 推荐17 推荐19 推荐21 推荐23 推荐25 推荐27 推荐29 推荐31 推荐33 推荐35 推荐37 推荐39 推荐41 推荐43 推荐45 推荐47 推荐49 关键词1 关键词101 关键词201 关键词301 关键词401 关键词501 关键词601 关键词701 关键词801 关键词901 关键词1001 关键词1101 关键词1201 关键词1301 关键词1401 关键词1501 关键词1601 关键词1701 关键词1801 关键词1901 视频扩展1 视频扩展6 视频扩展11 视频扩展16 文章1 文章201 文章401 文章601 文章801 文章1001 资讯1 资讯501 资讯1001 资讯1501 标签1 标签501 标签1001 关键词1 关键词501 关键词1001 关键词1501 专题2001
Javascript实现的数独解题算法网页实例_javascript技巧
2020-11-27 21:14:29 责编:小采
文档


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)当所有的路径搜索下来,答案不是唯一的情况,是和数独游戏的宗旨相悖的。

以下部分是全部代码,为方便阅读,调试信息未删除。

代码如下:


数独解题程序


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();
}
}







可以文本拷贝到下框中后点粘贴,从左到右从上往下的81个数字序列,未填为0,中间非数字字符将忽略




var maxRow =9;
var maxCol = 9;
var strTbody = ["

"];
for(var i = 0; i < maxRow; i++){
strTbody.push("");
for(var j = 0; j < maxCol; j++){
strTbody.push("");
}
strTbody.push("");
}
strTbody.push("
");
var sTbody = [""];
for(var i = 0; i < maxRow; i++){
sTbody.push("");
for(var j = 0; j < maxCol; j++){
sTbody.push("");
}
sTbody.push("");
}
sTbody.push("
");
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< columns[y] |= 1< 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();
}

建议使用IE浏览器,F12开启调试模式






下载本文
显示全文
专题