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 相关文章推荐
javascript中处理时间戳为日期格式的方法
Jan 02 Javascript
七个很有意思的PHP函数
May 12 Javascript
javascript的事件触发器介绍的实现
Jun 05 Javascript
js数组的基本操作(很全自己整理的)
Oct 16 Javascript
javascript实现节点(div)名称编辑
Dec 17 Javascript
JavaScript函数节流概念与用法实例详解
Jun 20 Javascript
jQuery实现ToolTip元素定位显示功能示例
Nov 23 Javascript
Angularjs cookie 操作实例详解
Sep 27 Javascript
简述Angular 5 快速入门
Nov 04 Javascript
解决Mac安装thrift因bison报错的问题
May 17 Javascript
使用jQuery动态设置单选框的选中效果
Dec 06 jQuery
Node.js在图片模板上生成二维码图片并附带底部文字说明实现详解
Aug 07 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下获取客户端ip地址的函数
2010/03/15 PHP
thinkphp数据查询和遍历数组实例
2014/11/28 PHP
php使用ftp实现文件上传与下载功能
2017/07/21 PHP
阿里云的WindowsServer2016上部署php+apache
2018/07/17 PHP
Jquery跨域获得Json时invalid label错误的解决办法
2011/01/11 Javascript
javascript的parseFloat()方法精度问题探讨
2013/11/26 Javascript
js获取当前路径的简单示例代码
2014/01/08 Javascript
node.js实现多图片上传实例
2014/06/03 Javascript
javascript动态创建表格及添加数据实例详解
2015/05/13 Javascript
javascript多物体运动实现方法分析
2016/01/08 Javascript
JS实现复制内容到剪贴板功能
2017/02/05 Javascript
jQuery的三种bind/One/Live/On事件绑定使用方法
2017/02/23 Javascript
详解如何在Vue2中实现组件props双向绑定
2017/03/29 Javascript
解决html input验证只能输入数字,不能输入其他的问题
2017/07/21 Javascript
JS实现自定义弹窗功能
2018/08/08 Javascript
使用weixin-java-tools完成微信授权登录、微信支付的示例
2018/09/26 Javascript
vue安装遇到的5个报错及解决方法
2019/06/12 Javascript
javascript如何实现create方法
2019/11/04 Javascript
vue中v-for循环选中点击的元素并对该元素添加样式操作
2020/07/17 Javascript
将字典转换为DataFrame并进行频次统计的方法
2018/04/08 Python
python实现根据文件关键字进行切分为多个文件的示例
2018/12/10 Python
python检测服务器端口代码实例
2019/08/31 Python
python系列 文件操作的代码
2019/10/06 Python
Python实现直播推流效果
2019/11/26 Python
Python实现动态给类和对象添加属性和方法操作示例
2020/02/29 Python
python 画条形图(柱状图)实例
2020/04/24 Python
Jupyter打开图形界面并画出正弦函数图像实例
2020/04/24 Python
Django自带用户认证系统使用方法解析
2020/11/12 Python
Python 实现PS滤镜中的径向模糊特效
2020/12/03 Python
L*SPACE官网:比基尼、泳装和度假服装
2019/03/18 全球购物
现金会计岗位职责
2013/12/05 职场文书
怎样写好创业计划书的内容
2014/02/06 职场文书
授权委托书样本
2014/09/25 职场文书
初三语文教学反思
2016/03/03 职场文书
Nginx开启Brotli压缩算法实现过程详解
2021/03/31 Servers
电脑无法安装Windows 11怎么办?无法安装Win11的解决方法
2021/11/21 数码科技