数独弄好了。可以尝试放数字。理论上可以遍历所有解。定义初始全为空,这个程序会一个一个放数字例如:
你说这个有解吗?我正在运算中已经几分钟了,还没结果。。先贴下HTML界面部分。竟然有解。。。
里面好像有0,这个结果不对。
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<meta name="renderer" content="webkit">
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
<meta name="viewport" content="width=540">
<meta name="apple-mobile-web-app-capable" content="yes">
<meta name="format-detection" content="telephone=no">
<meta name="applicable-device" content="pc">
</head>
<body >
<!-- head,css -->
<style type="text/css">
html,body,ul,li div{margin: 0;padding:0;}
.clearfix:after{content:"";display:block;clear:both;}
.clearfix{zoom:1;}
.box_bg {width: 400px;height: 396px;background-color: #7f5a0f;margin: 0 auto;padding-top: 20px;padding-bottom: 20px; }
.box_bg ul {width: 380px;margin: 0 auto;border:2px solid #ab8d17;background-color: #fff;}
.box_bg ul li {display: block;float: left;width: 40px;height: 40px;border:1px solid #d6c079;line-height: 40px;text-align: center;font-size:26px;color:#383838;font-weight: bold;}
.box_bg ul li.border_right{border-right:2px solid #c19502;}
.box_bg ul li.border_bottom{border-bottom:2px solid #c19502;}
.box_bg .on{background-color: #ffe000;}
.box_bg .red1{background-color: #d44b6a;}
.box_bg .red2{background-color: #e40e3e;}
.box_bg .red3{background-color: #f588a0;}
#selectbox { z-index: 1000;width: 115px;height: 152px;background-color: #deecfd;margin: 0 auto; position: absolute; display: none; }
#selectbox ul {width: 112px;margin: 0 auto;border:2px solid #deecfd;}
#selectbox ul li {display: block;float: left;width: 35px;height: 35px;border:1px solid #d6c079;line-height: 35px;text-align: center;font-size:20px;color:#383838;font-weight: bold;}
#selectbox ul li.border_right{border-right:2px solid #c19502;}
#selectbox ul li.border_bottom{border-bottom:2px solid #c19502;}
.maybebox { width: 39px;height: 38px;background-color: #deecfd;margin: 0 auto; position: absolute; display: none; }
.maybebox ul {width: 39px;margin: 0 auto; }
.maybebox ul li {display: block;float: left;width: 13px;height: 13px; line-height: 15px;text-align: center;font-size:10px;color:#383838;font-weight: bold;}
.maybebox ul li.border_right{border-right:2px solid #c19502;}
.maybebox ul li.border_bottom{border-bottom:2px solid #c19502;}
.button { z-index: 10;width: 484px;height: 35px;background-color: #deecfd;margin: 0 auto; position: absolute; }
.button ul {width: 483px;margin: 0 auto;border:2px solid #deecfd;}
.button ul li {display: block;float: left;width: 159px;height: 31px;border:1px solid #d6c079;line-height: 35px;text-align: center;font-size:15px;color:#383838;font-weight: bold;}
</style>
<script type="text/javascript" src="/lib/jquery-3.4.1/jquery-3.4.1.min.js"></script>
<!-- body -->
<div id="shudubox" class="box_bg"></div>
<div id="selectbox" targetid="" >
<ul>
<li>1</li>
<li>2</li>
<li>3</li>
<li>4</li>
<li>5</li>
<li>6</li>
<li>7</li>
<li>8</li>
<li>9</li>
<li style=" font-size: 17px;">清除</li><li></li><li style=" font-size: 17px;">关闭</li>
</ul>
</div>
<div id="maybebox100" class="maybebox">
<ul>
<li></li>
<li></li>
<li></li>
<li></li>
<li></li>
<li></li>
<li></li>
<li></li>
<li></li>
</ul>
</div>
<div class="button" >
<ul>
<li></li>
<li>开始计算</li>
<li></li>
<li></li>
<li></li>
<li></li>
</ul>
</div>
然后是suDu.js
var log = function(x){
console.log(x);
}
var n = 0; //统计次数
var suDu = window.sd || {};
suDu = (function(g) {
// suDu是便于搜索。
//存放数独题目
var setData = function (data){
this.data = data;
}
// 安装行列块计算可能的数字
var maybiNumber = function (index){
var data=suDu.data.split("");
var maybeNumber = "";
if(data[index]==0){
var current_H = parseInt(index/9) ; //行
var current_L = parseInt(index%9) ; //列
var current_B = parseInt(current_H/3) *3 + parseInt(current_L/3);//块
maybeNumber = "123456789";
for (var i = 0; i < data.length; i++) {
if( (parseInt(i/9) == current_H)
|| (parseInt(i%9) == current_L)
|| (parseInt(parseInt(i/9)/3) *3 + parseInt(parseInt(i%9)/3) == current_B) ){
if(data[i]>0){
maybeNumber = maybeNumber.replace(data[i],"");
}
}
}
}
return maybeNumber;
}
// 画可能盘
var createMaybePan = function (index) {
//检查为空才需要Mpan
var data=suDu.data.split("");
$("#maybebox" + index).remove();
if(data[index]==0){
// 制作可能框
var mb = $("#maybebox100").clone();
$(mb).attr("id","maybebox" + index);
$(mb).css("display","block");
$("body").append(mb);
// 定位
var shuduboxlix = $("#shudubox li").eq(index) ;
$(mb).offset({left: $(shuduboxlix).offset().left,top: $(shuduboxlix).offset().top});
// var mn = $(shuduboxlix).attr("mb").split("");
var mn = suDu.maybiNumber(index).split("");
for (let i = 0; i < mn.length; i++) {
$("#maybebox" + index + " li").eq(mn[i]-1).html(mn[i]);
}
}
}
var fillDataHtml = function (index,data) {
var shuduboxli = $("#shudubox li") ;
$(shuduboxli[index]).html((data=="0")?"":data);
suDu.createMaybePan(index);
}
// 填写一个数字并删除可能盘
var fillData = function(index,data){
var arrd = suDu.data.split("");
arrd[index]= data;
suDu.setData(arrd.join(""));
// fillDataHtml(index,data)
// log("填写第"+index+"格:"+data );
//往格子里填数字
//往空格里填写可能的数字
// $(shuduboxli[index]).attr("mb",suDu.maybiNumber(index));
}
var checkOneMaybe = function(params) {
var hasOne = false ;
for (var i = 0; i < 81; i++) {
// 检查 只有1个可能数,填写,并更新data
var maybei = suDu.maybiNumber(i);
if(maybei.length==1){
suDu.fillData( i ,maybei);
hasOne = true;
}
}
if(hasOne){
checkOneMaybe()
}
}
var checkHlbMaybe = function(params) {
var hasOne = false ;
var h={H:[]};
for (let index = 0; index < 27; index++) {
var hlb = "L";
var n = index-8;
if(index < 9 ){hlb="H";n=index+1;}
if(index > 17){hlb="B" ; n = index - 17}
h.H.push({name:hlb,i:n,v:"",k:'',o:[]});
}
// 统计行列块的备选数合并
var getHLB = function ( index) {
return h.H[index].v ;
}
var setHLB = function( index,value,i,maybin ) {
h.H[index].v= value ;
h.H[index].o.push({index:i,mb:maybin})
}
for (let i = 0; i < 81; i++) {
var index = i ;
var current_H = parseInt(index/9) ; //行
var current_L = parseInt(index%9) ; //列
var current_B = parseInt(current_H/3) *3 + parseInt(current_L/3);//块
var maybin = suDu.maybiNumber(i) ;
setHLB( current_H, getHLB( current_H) + maybin ,i,maybin )
setHLB(9+current_L, getHLB(9+current_L) + maybin,i,maybin )
setHLB(18+ current_B, getHLB(18+ current_B) +maybin ,i,maybin )
// const element = array[i];
}
//只有先合起来才能开始分析是否有只有1个备选的。
//备选k填写进来
for (let index = 0; index < h.H.length; index++) {
var _arr = h.H[index].v.split("") ;
_arr.sort();
var _res = []
for (var i = 0; i < _arr.length;) {
var count = 0;
for (var j = i; j < _arr.length; j++) {
if (_arr[i] == _arr[j]) {
count++;
}
}
if(count==1){
_res.push([_arr[i], count]);
h.H[index].k=_arr[i];
}
i += count;
}
}
for (let index = 0; index < h.H.length; index++) {
if( h.H[index].k.length>0 ){
h.H[index].o.forEach((v)=>{
if(v.mb.indexOf(h.H[index].k)>=0){
suDu.fillData( v.index ,h.H[index].k);
hasOne=true;
}
})
}
}
var data = suDu.data.split("");
for (var index = 0; index < 81; index ++) {
suDu.fillData( index ,data[ index ]);
}
suDu.hlb=h;
if(hasOne){
checkHlbMaybe();
}
}
var cal = function(params) {
// 找到所有备选中最短的。
var historyLog = suDu.historyLog || [];
//获取备选中最短的index,mabe.并拆分成可能放到his中
var minMb = {index:-1,mb:"11111111111111111111"}
var checkHasMaybe = function (params) {
var result = true;
for (let i = 0; i < 81; i++) {
if (suDu.data[i]==0){
if(suDu.maybiNumber(i).length==0){
result = false;
}
}
}
return result;
}
if(checkHasMaybe()){
for (let i = 0; i < 81; i++) {
var myb = suDu.maybiNumber(i) ;
if(myb.length<minMb.mb.length && myb.length>0 ){
minMb.index = i;
minMb.mb = myb;
}
}
}
var arr_minMb = minMb.mb.split("");
for (let i = 0; i < arr_minMb.length; i++) {
if(minMb.index>=0){
historyLog.push({
"data" : suDu.data,
"checkWrong":true,
"index" :minMb.index,
"number":arr_minMb[i]});
}
}
if(historyLog.length>0){
n++;
log("第" + n + "次假定在:" + historyLog[historyLog.length-1].index + "的位置填写:" + historyLog[historyLog.length-1].number)
suDu.setData(historyLog[historyLog.length-1].data);
suDu.fillData(historyLog[historyLog.length-1].index,historyLog[historyLog.length-1].number)
// log(historyLog)
delete historyLog[historyLog.length-1]
var newHis = [];
historyLog.forEach((v)=>{
newHis.push(v);
})
suDu.historyLog = newHis;
//现在开始尝试
suDu.checkOneMaybe();
suDu.checkHlbMaybe();// 检查行列块中是否有:只有一个备选数
if(suDu.total405(suDu.data)){
log(suDu.data);
}else{
setTimeout(function (params) {
suDu.cal();
},0)
}
}
}
// 函数的方法
// var fillData = function(index,data){
// var shuduboxli = $("#shudubox li") ;
// //往格子里填数字
// $(shuduboxli[index]).html((data=="0")?"":data);
// //往空格里填写可能的数字
// // $(shuduboxli[index]).attr("mb",suDu.maybiNumber(index));
// suDu.createMaybePan(index);
// }
// 有返回值的范例
// var fillData = (function(index,data) {
// var shuduboxli = $("#shudubox li") ;
// $(shuduboxli[index]).html((data=="0")?"":data);
// return { init: "1" }
// } )();
var createBlank = (function() {
var _tmp = "<ul class='clearfix'>";
for (var i = 0; i < 9; i++) {
for (var j = 0; j < 9; j++) {
_tmp += "<li class='"+ (( i==2 || i==5 )?"border_bottom":"") + (((j+1)%3==0 && j!=8)?" border_right":"") +"'>" +"</li>";
}
}
_tmp += "</ul>";
return _tmp;
} )();
var total405 = function(data) {
var result = false ;
var count=0;
var d = data.split("");
d.forEach((v)=>{
// if(v==0){return false;}
count += parseInt(v);
})
if(count==405){result = true;}
return result;
}
return {
createBlank: createBlank ,
createMaybePan : createMaybePan,
data : this.data,
setData : setData,
maybiNumber:maybiNumber,
fillData:fillData,
checkOneMaybe:checkOneMaybe,
checkHlbMaybe:checkHlbMaybe,
total405:total405,
cal:cal
}
})(jQuery);
然后是启动部分。
// 000010000008006004020000090000000270300000000604005000000070000005000003000920000
// 946812537758396124123457698589134276312769845674285319431578962295641783867923451
// 946812537758396124123457698589134276312769845674285319431578962295641783867923451
// 328226
// 010800005060030080200000060004007000030000100000500002300040900800000040009006300
// 413862795967435281285971463624317859538629174791584632356248917872193546149756328
// 438226
// // var startdata ="000970000340000000806000005107095086500400200930602100712009560605724813483100970"; //
// var startdata ="004602000600030004020400090980040350103000040460003807030024070040061035009300400"; // 25197364834956872187624139512739548656841723993468215771283956469572481348315697218250"; //一道初级1
// // http://www.cn.sudokupuzzle.org/printable.php?nd=1&y=2017&m=11&d=1 题库可用
// // http://www.cn.sudokupuzzle.org/online2.php?nd=0&y=2017&m=11&d=25
// // 1995-5-1~2019-12-26 共23*365*5 = 42000 题 带答案 http://www.cn.sudokupuzzle.org/online2.php?nd=0&y=2017&m=11&d=25
// // 标准数独应该有6670903752021072936960种终盘,
// // 如果将重复(如数字交换、对称等)不计算,那么有5,472,730,538个,
// 123456789200000000300000000400000000500000000600000000700000000800000000987645321
suDu.setData("123456789200000000300000000400000000500000000600000000700000000800000000987645321");
$("#shudubox").html(suDu.createBlank); //createBlank 画盘子
var data = suDu.data.split(""); // 填充Data数
for (var index = 0; index < 81; index ++) {
suDu.fillData( index ,data[ index ]);
}
setTimeout(function (params) {
suDu.checkOneMaybe();
suDu.checkHlbMaybe();// 检查行列块中是否有:只有一个备选数
},50)
// 按钮事件
$(".button li").click(function (e) {
// 第二按钮,检查整行列块是否有1个可能数
if($(this).index()==1){
suDu.cal()
}
})
已经3000次了不知道能否有结果。
用这个程序已经可以解开大师级的难题了。
短短16步假定,是如何做到的呢?假定前的工作是已经放好的。
最后放出本程序最终版本:建议从后面看起。
var log = function(x){
console.log(x);
}
var n = 0; //统计次数
var suDu = window.sd || {};
suDu = (function() {
// suDu是便于搜索。
//存放数独题目,通过suDu.data来获取当前的数字。setData来回溯数据
var setData = function (data){
this.data = data;
}
// 可能的数字: index位置来计算这个格子可能的数字。
// 如果为空则无解。如果只有一个,就可以直接填写。
var maybiNumber = function (index){
var data=suDu.data.split(""); //获取数组
var maybeNumber = "";
if(data[index]==0){ //检查是否是空格,有数字的就不用再计算
var current_H = parseInt(index/9) ; //行属
var current_L = parseInt(index%9) ; //列属
var current_B = parseInt(current_H/3) *3 + parseInt(current_L/3);//块属
maybeNumber = "123456789"; //初始可能的数字串
for (var i = 0; i < data.length; i++) {
if( (parseInt(i/9) == current_H)
|| (parseInt(i%9) == current_L)
|| (parseInt(parseInt(i/9)/3) *3 + parseInt(parseInt(i%9)/3) == current_B) ){
if(data[i]>0){ //逐个取出并去掉行列块中已经存在的数字,剩下的就是可能的数字
maybeNumber = maybeNumber.replace(data[i],"");
}
}
}
}
return maybeNumber;
}
//改变suDu.data 用给定位置,数字
var fillOneData = function (index,data) {
suDu.setData( suDu.data.substr(0,parseInt(index)) + data + suDu.data.substr( parseInt(index) +1 ) );
}
//这个是发现前面检查后还有漏的。
// 这个就是说一行列块中的可能数全部放一起排序并统计,如果有数量为1的可能数。
// 整行只有1个格子里有这个数,那这个格子就是这个数,其他格子都不可能是这个数
var checkHLBMaybe = function() {
var h={H:[]};
for (let index = 0; index < 27; index++) {
var hlb = "L";
var n = index-8;
if(index < 9 ){hlb="H";n=index+1;}
if(index > 17){hlb="B" ; n = index - 17}
h.H.push({name:hlb,i:n,v:"",k:'',o:[]});
}
// 统计行列块的备选数合并
var getHLB = function ( index) {
return h.H[index].v ;
}
var setHLB = function( index,value,i,maybin ) {
h.H[index].v= value ;
h.H[index].o.push({index:i,mb:maybin})
}
for (let i = 0; i < 81; i++) {
var index = i ;
var current_H = parseInt(index/9) ; //行
var current_L = parseInt(index%9) ; //列
var current_B = parseInt(current_H/3) *3 + parseInt(current_L/3);//块
var maybin = suDu.maybiNumber(i) ;
setHLB( current_H, getHLB( current_H) + maybin ,i,maybin )
setHLB(9+current_L, getHLB(9+current_L) + maybin,i,maybin )
setHLB(18+ current_B, getHLB(18+ current_B) +maybin ,i,maybin )
// const element = array[i];
}
//只有先合起来才能开始分析是否有只有1个备选的。上面这些事是必须的。
//备选k填写进来
for (let index = 0; index < 27; index++) {
var _arr = h.H[index].v.split("") ;
_arr.sort();
for (var i = 0; i < _arr.length;) {
var count = 0;
for (var j = i; j < _arr.length; j++) {
if (_arr[i] == _arr[j]) {
count++;
}
}
if(count==1){
h.H[index].k=_arr[i];
}
i += count;
}
}
var result ="";
for (let index = 0; index < h.H.length; index++) {
if( h.H[index].k.length>0 ){
h.H[index].o.forEach((v)=>{
// 前面每次setHLB的时候是这样放进去的
// h.H[index].o.push({index:i,mb:maybin})
if(v.mb.indexOf(h.H[index].k)>=0){
result= v.index + "_" + h.H[index].k ;
}
})
}
}
return result ;
}
//检查备选中是否有可用的数字,并更新之,且循环。
var checkOneMaybe = function(params) {
var hasOne = false ;
for (var i = 0; i < 81; i++) {
// 检查 只有1个可能数,填写,并更新data
var maybei = suDu.maybiNumber(i);
if(maybei.length==1){
suDu.fillOneData( i ,maybei);
hasOne = true; //再次计算并检查。
}
var hlbMaybi = suDu.checkHLBMaybe(i); //返回 "0_9"
if(hlbMaybi.length>0){
suDu.fillOneData( hlbMaybi.split("_")[0],hlbMaybi.split("_")[1]);
hasOne = true; //再次计算并检查。
}
}
if(hasOne){
checkOneMaybe();
}
}
// 画可能盘,
var createMaybePan = function (index) {
//检查为空才需要Mpan
var data=suDu.data.split("");
$("#maybebox" + index).remove();
if(data[index]==0){
// 制作可能框
var mb = $("#maybebox100").clone();
$(mb).attr("id","maybebox" + index);
$(mb).css("display","block");
$("body").append(mb);
// 定位
var shuduboxlix = $("#shudubox li").eq(index) ;
$(mb).offset({left: $(shuduboxlix).offset().left,top: $(shuduboxlix).offset().top});
// var mn = $(shuduboxlix).attr("mb").split("");
var mn = suDu.maybiNumber(index).split("");
for (let i = 0; i < mn.length; i++) {
$("#maybebox" + index + " li").eq(mn[i]-1).html(mn[i]);
}
}
}
var fillDataHtml = function (index,data) {
var shuduboxli = $("#shudubox li") ;
$(shuduboxli[index]).html((data=="0")?"":data);
// suDu.createMaybePan(index);
}
// 填写一个数字并删除可能盘
var fillData = function(index,data){
var arrd = suDu.data.split("");
arrd[index]= data;
suDu.setData(arrd.join(""));
fillDataHtml(index,data)
// log("填写第"+index+"格:"+data );
//往格子里填数字
//往空格里填写可能的数字
// $(shuduboxli[index]).attr("mb",suDu.maybiNumber(index));
}
var cal = function(params) {
// 找到所有备选中最短的进行回溯,最好是2个数的。
var historyLog = suDu.historyLog || [];
//获取备选中最短的index,mabe.并拆分成可能放到his中
var minMb = {index:-1,mb:"123456789"} ; //初始化最小位置和可能数
//如果计算到有备选为空的情况说明前面有填错的了。
var noBlankMaybe = function (params) {
var result = true;
for (let i = 0; i < 81; i++) {
if (suDu.data[i]==0){
if(suDu.maybiNumber(i).length==0){
result = false;
}
}
}
return result;
}
//挑出最短的可选进行尝试、
if(noBlankMaybe()){
for (let i = 0; i < 81; i++) {
var myb = suDu.maybiNumber(i) ;
if(myb.length<minMb.mb.length && myb.length>0 ){
minMb.index = i;
minMb.mb = myb;
}
}
}
//尝试之前备份数据,和可以尝试的备选
var arr_minMb = minMb.mb.split("");
for (let i = 0; i < arr_minMb.length; i++) {
if(minMb.index>=0){
historyLog.push({
"data" : suDu.data,
"checkWrong":true,
"index" :minMb.index,
"number":arr_minMb[i]});
}
}
if(historyLog.length>0){
n++;
log("第" + n + "次假定在:" + historyLog[historyLog.length-1].index + "的位置填写:" + historyLog[historyLog.length-1].number)
suDu.setData(historyLog[historyLog.length-1].data);
//如果有假定数,尝试填写一个。用后并删除它。要么成功。要么结束,回来继续填写没用过的。
suDu.fillOneData(historyLog[historyLog.length-1].index,historyLog[historyLog.length-1].number)
// log(historyLog)
delete historyLog[historyLog.length-1]
var newHis = [];
historyLog.forEach((v)=>{
newHis.push(v);
})
suDu.historyLog = newHis;
//现在开始尝试
if(noBlankMaybe()){
suDu.checkOneMaybe();
// suDu.checkHlbMaybe();// 检查行列块中是否有:只有一个备选数
if(suDu.total405(suDu.data)){
log(suDu.data);
}else{
setTimeout(function (params) {
suDu.cal();
},0)
}
}
}
}
// 函数的方法
// var fillData = function(index,data){
// var shuduboxli = $("#shudubox li") ;
// //往格子里填数字
// $(shuduboxli[index]).html((data=="0")?"":data);
// //往空格里填写可能的数字
// // $(shuduboxli[index]).attr("mb",suDu.maybiNumber(index));
// suDu.createMaybePan(index);
// }
// 有返回值的范例
// var fillData = (function(index,data) {
// var shuduboxli = $("#shudubox li") ;
// $(shuduboxli[index]).html((data=="0")?"":data);
// return { init: "1" }
// } )();
var createBlank = (function() {
var _tmp = "<ul class='clearfix'>";
for (var i = 0; i < 9; i++) {
for (var j = 0; j < 9; j++) {
_tmp += "<li class='"+ (( i==2 || i==5 )?"border_bottom":"") + (((j+1)%3==0 && j!=8)?" border_right":"") +"'>" +"</li>";
}
}
_tmp += "</ul>";
return _tmp;
} )();
var total405 = function(data) {
var result = false ;
var count=0;
var d = data.split("");
d.forEach((v)=>{
// if(v==0){return false;}
count += parseInt(v);
})
if(count==405){result = true;}
return result;
}
return {
createBlank: createBlank ,
createMaybePan : createMaybePan,
data : this.data,
setData : setData,
maybiNumber:maybiNumber,
fillData:fillData,
checkOneMaybe:checkOneMaybe,
checkHLBMaybe:checkHLBMaybe,
total405:total405,
cal:cal ,
fillOneData:fillOneData,
fillDataHtml:fillDataHtml
}
})(jQuery);
//只用初级的解题可以解初级的。
//"000970000340000000806000005107095086500400200930602100712009560605724813483100970
//"251973648349568721876241395127395486568417239934682157712839564695724813483156972"
// 下面这个大师级的就无法解,还是要用回溯法
// suDu.setData("004602000600030004020400090980040350103000040460003807030024070040061035009300400");
// "004602000600030004020400090980040350103000040460003807036024070040061035019300460"
// 394672581651839724827415693982147356173586249465293817536924178248761935719358462
//回溯16次后解出答案
suDu.setData("123456789456789123789123456231000000564000000891000000312000000645000000978000000");
$("#shudubox").html(suDu.createBlank);
var data = suDu.data.split(""); // 填充Data数
for (var index = 0; index < 81; index ++) {
suDu.fillDataHtml( index ,data[ index ]);
}
// "004602000600030004020400090980040350103000040460003807036024070040061035019300460"
suDu.checkOneMaybe();
suDu.cal();
log(suDu);