javascript解三阶幻方(九宫格)


Posted in Javascript onApril 22, 2015

谜题:三阶幻方, 试将1~9这9个不同整数填入一个3×3的表格,使得每行、每列以及每条对角线上的数字之和相同。

策略:穷举搜索。列出所有的整数填充方案,然后进行过滤。

亮点为递归函数getPermutation的设计,文章最后给出了几个非递归算法

// 递归算法,很巧妙,但太费资源
function getPermutation(arr) {
  if (arr.length == 1) {
    return [arr];
  }
  var permutation = [];
  for (var i = 0; i < arr.length; i++) {
    var firstEle = arr[i];         //取第一个元素
    var arrClone = arr.slice(0);      //复制数组
    arrClone.splice(i, 1);         //删除第一个元素,减少数组规模
    var childPermutation = getPermutation(arrClone);//递归
    for (var j = 0; j < childPermutation.length; j++) {
      childPermutation[j].unshift(firstEle);   //将取出元素插入回去
    }
    permutation = permutation.concat(childPermutation);
  }
  return permutation;
}

function validateCandidate(candidate) {
  var sum = candidate[0] + candidate[1] + candidate[2];
  for (var i = 0; i < 3; i++) {
    if (!(sumOfLine(candidate, i) == sum && sumOfColumn(candidate, i) == sum)) {
      return false;
    }
  }
  if (sumOfDiagonal(candidate, true) == sum && sumOfDiagonal(candidate, false) == sum) {
    return true;
  }
  return false;
}
function sumOfLine(candidate, line) {
  return candidate[line * 3] + candidate[line * 3 + 1] + candidate[line * 3 + 2];
}
function sumOfColumn(candidate, col) {
  return candidate[col] + candidate[col + 3] + candidate[col + 6];
}
function sumOfDiagonal(candidate, isForwardSlash) {
  return isForwardSlash ? candidate[2] + candidate[4] + candidate[6] : candidate[0] + candidate[4] + candidate[8];
}

var permutation = getPermutation([1, 2, 3, 4, 5, 6, 7, 8, 9]);
var candidate;
for (var i = 0; i < permutation.length; i++) {
  candidate = permutation[i];
  if (validateCandidate(candidate)) {
    break;
  } else {
    candidate = null;
  }
}
if (candidate) {
  console.log(candidate);
} else {
  console.log('No valid result found');
}

//求模(非递归)全排列算法

/*
算法的具体示例:
*求4个元素["a", "b", "c", "d"]的全排列, 共循环4!=24次,可从任意>=0的整数index开始循环,每次累加1,直到循环完index+23后结束;
*假设index=13(或13+24,13+224,13+3*24…),因为共4个元素,故迭代4次,则得到的这一个排列的过程为:
*第1次迭代,13/1,商=13,余数=0,故第1个元素插入第0个位置(即下标为0),得["a"];
*第2次迭代,13/2, 商=6,余数=1,故第2个元素插入第1个位置(即下标为1),得["a", "b"];
*第3次迭代,6/3, 商=2,余数=0,故第3个元素插入第0个位置(即下标为0),得["c", "a", "b"];
*第4次迭代,2/4,商=0,余数=2, 故第4个元素插入第2个位置(即下标为2),得["c", "a", "d", "b"];
*/

function perm(arr) {
  var result = new Array(arr.length);
  var fac = 1;
  for (var i = 2; i <= arr.length; i++)  //根据数组长度计算出排列个数
    fac *= i;
  for (var index = 0; index < fac; index++) { //每一个index对应一个排列
    var t = index;
    for (i = 1; i <= arr.length; i++) {   //确定每个数的位置
      var w = t % i;
      for (var j = i - 1; j > w; j--)   //移位,为result[w]留出空间
        result[j] = result[j - 1];
      result[w] = arr[i - 1];
      t = Math.floor(t / i);
    }
    if (validateCandidate(result)) {
      console.log(result);
      break;
    }
  }
}
perm([1, 2, 3, 4, 5, 6, 7, 8, 9]);
//很巧妙的回溯算法,非递归解决全排列

function seek(index, n) {
  var flag = false, m = n; //flag为找到位置排列的标志,m保存正在搜索哪个位置,index[n]为元素(位置编码)
  do {
    index[n]++;    //设置当前位置元素
    if (index[n] == index.length) //已无位置可用
      index[n--] = -1; //重置当前位置,回退到上一个位置
    else if (!(function () {
        for (var i = 0; i < n; i++)  //判断当前位置的设置是否与前面位置冲突
          if (index[i] == index[n]) return true;//冲突,直接回到循环前面重新设置元素值
        return false;  //不冲突,看当前位置是否是队列尾,是,找到一个排列;否,当前位置后移
      })()) //该位置未被选择
      if (m == n) //当前位置搜索完成
        flag = true;
      else
        n++;  //当前及以前的位置元素已经排好,位置后移
  } while (!flag && n >= 0)
  return flag;
}
function perm(arr) {
  var index = new Array(arr.length);
  for (var i = 0; i < index.length; i++)
    index[i] = -1;
  for (i = 0; i < index.length - 1; i++)
    seek(index, i);  //初始化为1,2,3,...,-1 ,最后一位元素为-1;注意是从小到大的,若元素不为数字,可以理解为其位置下标
  while (seek(index, index.length - 1)) {
    var temp = [];
    for (i = 0; i < index.length; i++)
      temp.push(arr[index[i]]);
    if (validateCandidate(temp)) {
      console.log(temp);
      break;
    }
  }
}
perm([1, 2, 3, 4, 5, 6, 7, 8, 9]);

/*
全排列(非递归求顺序)算法
1、建立位置数组,即对位置进行排列,排列成功后转换为元素的排列;
2、按如下算法求全排列:
设P是1~n(位置编号)的一个全排列:p = p1,p2...pn = p1,p2...pj-1,pj,pj+1...pk-1,pk,pk+1...pn
(1)从排列的尾部开始,找出第一个比右边位置编号小的索引j(j从首部开始计算),即j = max{i | pi < pi+1}
(2)在pj的右边的位置编号中,找出所有比pj大的位置编号中最小的位置编号的索引k,即 k = max{i | pi > pj}
pj右边的位置编号是从右至左递增的,因此k是所有大于pj的位置编号中索引最大的
(3)交换pj与pk
(4)再将pj+1...pk-1,pk,pk+1...pn翻转得到排列p' = p1,p2...pj-1,pj,pn...pk+1,pk,pk-1...pj+1
(5)p'便是排列p的下一个排列

例如:
24310是位置编号0~4的一个排列,求它下一个排列的步骤如下:
(1)从右至左找出排列中第一个比右边数字小的数字2;
(2)在该数字后的数字中找出比2大的数中最小的一个3;
(3)将2与3交换得到34210;
(4)将原来2(当前3)后面的所有数字翻转,即翻转4210,得30124;
(5)求得24310的下一个排列为30124。
*/

function swap(arr, i, j) {
  var t = arr[i];
  arr[i] = arr[j];
  arr[j] = t;

}
function sort(index) {
  for (var j = index.length - 2; j >= 0 && index[j] > index[j + 1]; j--)
    ; //本循环从位置数组的末尾开始,找到第一个左边小于右边的位置,即j
  if (j < 0) return false; //已完成全部排列
  for (var k = index.length - 1; index[k] < index[j]; k--)
    ; //本循环从位置数组的末尾开始,找到比j位置大的位置中最小的,即k
  swap(index, j, k);
  for (j = j + 1, k = index.length - 1; j < k; j++, k--)
    swap(index, j, k); //本循环翻转j+1到末尾的所有位置
  return true;
}
function perm(arr) {
  var index = new Array(arr.length);
  for (var i = 0; i < index.length; i++)
    index[i] = i;
  do {
    var temp = [];
    for (i = 0; i < index.length; i++)
      temp.push(arr[index[i]]);
    if (validateCandidate(temp)) {
      console.log(temp);
      break;
    }
  } while (sort(index));
}
perm([1, 2, 3, 4, 5, 6, 7, 8, 9]);

以上所述就是本文的全部内容了,希望大家能够喜欢。

Javascript 相关文章推荐
jQuery Div中加载其他页面的实现代码
Feb 27 Javascript
jQuery实现form表单元素序列化为json对象的方法
Dec 09 Javascript
JS加载器如何动态加载外部js文件
May 26 Javascript
Bootstrap学习笔记之css样式设计(1)
Jun 07 Javascript
js验证手机号、密码、短信验证码代码工具类
Jun 24 Javascript
vue中的event bus非父子组件通信解析
Oct 27 Javascript
用Webpack构建Vue项目的实践
Nov 07 Javascript
Node.JS在命令行中检查Chrome浏览器是否安装并打开指定网址
May 21 Javascript
layui多图上传实现删除功能的例子
Sep 23 Javascript
vue 动态表单开发方法案例详解
Dec 02 Javascript
vue 修改 data 数据问题并实时显示操作
Sep 07 Javascript
用js实现放大镜效果
Oct 28 Javascript
javascript递归回溯法解八皇后问题
Apr 22 #Javascript
使用C++为node.js写扩展模块
Apr 22 #Javascript
node.js 使用ejs模板引擎时后缀换成.html
Apr 22 #Javascript
JavaScript模拟深蓝vs卡斯帕罗夫的国际象棋对局示例
Apr 22 #Javascript
jscript读写二进制文件的方法
Apr 22 #Javascript
javascript格式化json显示实例分析
Apr 21 #Javascript
JavaScript汉诺塔问题解决方法
Apr 21 #Javascript
You might like
PHP将两个关联数组合并函数提高函数效率
2014/03/18 PHP
PHP数组与对象之间使用递归实现转换的方法
2015/06/24 PHP
利用腾讯的ip地址库做ip物理地址定位
2010/07/24 Javascript
Javascript中arguments对象详解
2014/10/22 Javascript
js实现仿百度瀑布流的方法
2015/02/05 Javascript
微信小程序 框架详解及实例应用
2016/09/26 Javascript
easyui messager alert 三秒后自动关闭提示的实例
2016/11/07 Javascript
浅谈JS读取DOM对象(标签)的自定义属性
2016/11/21 Javascript
JavaScript中transform实现数字翻页效果
2017/03/08 Javascript
详解如何在Vue2中实现组件props双向绑定
2017/03/29 Javascript
js实现点击切换checkbox背景图片的简单实例
2017/05/08 Javascript
详解在express站点中使用ejs模板引擎
2017/09/21 Javascript
Nodejs下使用gm圆形裁剪并合成图片的示例
2018/02/22 NodeJs
小程序跨页面交互的作用与方法详解
2020/01/07 Javascript
vue+高德地图实现地图搜索及点击定位操作
2020/09/09 Javascript
在vue中通过render函数给子组件设置ref操作
2020/11/17 Vue.js
[43:43]完美世界DOTA2联赛PWL S2 FTD.C vs Rebirth 第一场 11.22
2020/11/24 DOTA
Python中的文件和目录操作实现代码
2011/03/13 Python
Python利用matplotlib生成图片背景及图例透明的效果
2017/04/27 Python
python使用matplotlib库生成随机漫步图
2018/08/27 Python
Python编程深度学习计算库之numpy
2018/12/28 Python
对Python3使运行暂停的方法详解
2019/02/18 Python
Python实现二叉树前序、中序、后序及层次遍历示例代码
2019/05/18 Python
使用Python完成15位18位身份证的互转功能
2019/11/06 Python
详解python 破解网站反爬虫的两种简单方法
2020/02/09 Python
Python使用pickle进行序列化和反序列化的示例代码
2020/09/22 Python
python使用requests库爬取拉勾网招聘信息的实现
2020/11/20 Python
用python读取xlsx文件
2020/12/17 Python
Bjorn Borg官方网上商店:国际运动时尚品牌
2016/08/27 全球购物
Monica Vinader官网:英国轻奢珠宝品牌
2020/02/05 全球购物
办理生育手续介绍信
2014/01/14 职场文书
银行员工辞职信范文
2014/01/20 职场文书
中秋节超市促销方案
2014/01/30 职场文书
观后感的写法
2015/06/19 职场文书
食品安全主题班会
2015/08/13 职场文书
2016五四青年节活动总结范文
2016/04/06 职场文书