【发布时间】:2016-03-04 23:29:10
【问题描述】:
要求:生成集合所有可能组合的算法,不重复,或递归调用函数返回结果。
Permutations in JavaScript? 提供的大部分(如果不是全部)答案会从循环或其他函数中递归调用函数以返回结果。
循环内递归函数调用示例
function p(a, b, res) {
var b = b || [], res = res || [], len = a.length;
if (!len)
res.push(b)
else
for (var i = 0; i < len
// recursive call to `p` here
; p(a.slice(0, i).concat(a.slice(i + 1, len)), b.concat(a[i]), res)
, i++
);
return res
}
p(["a", "b", "c"]);
当前问题尝试在线性过程中创建给定的排列,依赖于先前的排列。
例如,给定一个数组
var arr = ["a", "b", "c"];
确定可能排列的总数
for (var len = 1, i = k = arr.length; len < i ; k *= len++);
k 应该返回 6 或 arr ["a", "b", "c"] 的可能排列总数
在为 set 确定单个排列的总数后,可以使用 Array.prototype.slice() 、 Array.prototype.concat() 和 Array.prototype.reverse() 创建和填充包含所有六个排列的结果数组
var res = new Array(new Array(k));
res[0] = arr;
res[1] = res[0].slice(0,1).concat(res[0].slice(-2).reverse());
res[2] = res[1].slice(-1).concat(res[1].slice(0,2));
res[3] = res[2].slice(0,1).concat(res[2].slice(-2).reverse());
res[4] = res[3].slice(-2).concat(res[3].slice(0,1));
res[5] = res[4].slice(0,1).concat(res[4].slice(-2).reverse());
尝试根据图表上显示的模式重现结果,An Ordered Lexicographic Permutation Algorithm 基于在 C++ 中的实用算法Calculating Permutations and Job Interview Questions 上发表的算法。
如果输入集是 ,则似乎有一个可以扩展的模式,例如
["a", "b", "c", "d", "e"]
预计会有 120 个排列。
尝试仅依赖先前排列填充数组的示例
// returns duplicate entries at `j`
var arr = ["a", "b", "c", "d", "e"], j = [];
var i = k = arr.length;
arr.forEach(function(a, b, array) {
if (b > 1) {
k *= b;
if (b === i -1) {
for (var q = 0;j.length < k;q++) {
if (q === 0) {
j[q] = array;
} else {
j[q] = !(q % i)
? array.slice(q % i).reverse().concat(array.slice(0, q % i))
: array.slice(q % i).concat(array.slice(0, q % i));
}
}
}
}
})
但是还不能对.slice()、.concat()、.reverse() 以上js 的参数进行必要的调整,以从一种排列过渡到另一种排列;而仅使用res 中的前一个数组条目来确定当前排列,而不使用递归。
注意到偶数、奇数的调用平衡,并尝试使用模数 % 运算符和输入数组 .length 来调用 .reverse() 或不在 ["a", "b", "c", "d", "e"] 数组中调用,但没有产生没有重复条目的结果。
预期的结果是上述模式可以减少到在整个过程中连续调用两行,直到所有排列完成,res 填充;拨打.reverse()各一个,不拨打.reverse();例如,res[0] 填充后
// odd , how to adjust `.slice()` , `.concat()` parameters
// for array of unknown `n` `.length` ?
res[i] = res[i - 1].slice(0,1).concat(res[i - 1].slice(-2).reverse());
// even
res[i] = res[1 - 1].slice(-1).concat(res[i - 1].slice(0,2));
问题:需要对上述模式进行哪些调整,特别是参数或索引,通过.slice()、.concat() 生成给定集合的所有可能排列,而不使用对当前处理函数的递归调用?
var arr = ["a", "b", "c"];
for (var len = 1, i = k = arr.length; len < i; k *= len++);
var res = new Array(new Array(k));
res[0] = arr;
res[1] = res[0].slice(0, 1).concat(res[0].slice(-2).reverse());
res[2] = res[1].slice(-1).concat(res[1].slice(0, 2));
res[3] = res[2].slice(0, 1).concat(res[2].slice(-2).reverse());
res[4] = res[3].slice(-2).concat(res[3].slice(0, 1));
res[5] = res[4].slice(0, 1).concat(res[4].slice(-2).reverse());
console.log(res);
编辑、更新
已经找到了一种利用上述模式的过程,使用单个 for 循环以字典顺序为输入最多返回 .length 4 的排列。对于 .length 或 5 的数组,不会返回预期结果。
该模式基于“计算排列和求职面试问题”中的第二张图表[0]。
不希望使用.splice() 或.sort() 来返回结果,尽管在此处使用时尝试遵守每列的最后一个“旋转” 要求。变量r 应该引用下一个排列的第一个元素的index,它确实如此。
如果.splice()、.sort() 的使用遵循图表中的模式,则可以包括它们的使用;虽然在下面的js,他们实际上没有。
不完全确定下面js 的问题只是if (i % (total / len) === reset) 之后的声明,尽管这部分需要花费最多的时间;但仍未返回预期结果。
具体来说,现在参考图表,在旋转时,例如2 到索引0,1 到索引2。试图通过使用 r 来实现这一点,这是一个负索引,从右到左遍历以检索应该位于相邻“列”的 index0 的下一个项目。
在下一列,2 将放置在 index 2 ,3 将放置在 index 0。这是部分,就目前为止能够掌握或调试的情况而言,是发生错误的区域。
同样,返回 [1,2,3,4] 的预期结果,但不是 [1,2,3,4,5] 的预期结果
var arr = [1, 2, 3, 4];
for (var l = 1, j = total = arr.length; l < j ; total *= l++);
for (var i = 1
, reset = 0
, idx = 0
, r = 0
, len = arr.length
, res = [arr]
; i < total; i++) {
// previous permutation
var prev = res[i - 1];
// if we are at permutation `6` here, or, completion of all
// permutations beginning with `1`;
// setting next "column", place `2` at `index` 0;
// following all permutations beginning with `2`, place `3` at
// `index` `0`; with same process for `3` to `4`
if (i % (total / len) === reset) {
r = --r % -(len);
var next = prev.slice(r);
if (r === -1) {
// first implementation used for setting item at index `-1`
// to `index` 0
// would prefer to use single process for all "rotations",
// instead of splitting into `if` , `else`, though not there, yet
res[i] = [next[0]].concat(prev.slice(0, 1), prev.slice(1, len - 1)
.reverse());
} else {
// workaround for "rotation" at from `index` `r` to `index` `0`
// the chart does not actually use the previous permutation here,
// but rather, the first permutation of that particular "column";
// here, using `r` `,i`, `len`, would be
// `res[i - (i - 1) % (total / len)]`
var curr = prev.slice();
// this may be useful, to retrieve `r`,
// `prev` without item at `r` `index`
curr.splice(prev.indexOf(next[0]), 1);
// this is not optiomal
curr.sort(function(a, b) {
return arr.indexOf(a) > arr.indexOf(b)
});
// place `next[0]` at `index` `0`
// place remainder of sorted array at `index` `1` - n
curr.splice(0, 0, next[0])
res[i] = curr
}
idx = reset;
} else {
if (i % 2) {
// odd
res[i] = prev.slice(0, len - 2).concat(prev.slice(-2)
.reverse())
} else {
// even
--idx
res[i] = prev.slice(0, len - (len - 1))
.concat(prev.slice(idx), prev.slice(1, len + (idx)))
}
}
}
// try with `arr` : `[1,2,3,4,5]` to return `res` that is not correct;
// how can above `js` be adjusted to return correct results for `[1,2,3,4,5]` ?
console.log(res, res.length)
资源:
Generating Permutation with Javascript
(Countdown) QuickPerm Head Lexicography: (Formally Example_03 ~ Palindromes)
Generating all Permutations [non-recursive]
(尝试从C++ 移植到javascript jsfiddle http://jsfiddle.net/tvvvjf3p/)
Calculating Permutation without Recursion - Part 2
permutations of a string using iteration
Evaluation of permutation algorithms
Permutation algorithm without recursion? Java
Non-recursive algorithm for full permutation with repetitive elements?
String permutations in Java (non-recursive)
Generating permutations lazily
How to generate all permutations of a list in Python
Can all permutations of a set or string be generated in O(n log n) time?
【问题讨论】:
-
Quora: What's an algorithm to print the permutations of a string without recursion? 有一个算法可能会有所帮助。您还可以搜索其他语言的算法并将其转换为 ECMAScript。
-
@Spektre 如果可能,可以将解决方案制定为
javascript,发布为当前问题的答案,并描述过程、实施的方法? -
@guest271314 抱歉,我不使用 Javascript 编码这就是为什么我只使用评论,但该线程还包含描述它如何与示例一起使用,因此对于任何一种编码,移植到 javascript 都不应该太难在里面。
-
Steinhaus–Johnson–Trotter 算法可能会让您感兴趣,en.m.wikipedia.org/wiki/…
标签: javascript algorithm permutation