要求:算法以生成一组的所有可能的组合,没有重复,或递归调用函数返回的结果。
大多数,如果不是全部的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"]
可能的排列总数
用一组确定的个别置换的总数,所得的阵列,其将包含所有六个置换可以创建并填充使用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());
试图基于在基于一个在Calculating Permutations and Job Interview Questions发表在实用算法在C ++为有序排列辞书算法的图表显示的图案再现的结果。
似乎存在如果输入集合是可以扩展的图案,例如
["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);
编辑,更新
已经找到一种方法,利用上述返回在字典顺序排列,用于将输入到.length
4,使用单个for
环形图案。预期结果不返回与.length
的5
阵列。
所述图案在[0]“计算排列和面试问题”是基于在第二图表上。
宁愿不使用.splice()
或.sort()
返回结果,尽管这里使用,同时试图坚持到最后每列“旋转”的要求。可变r
应参照下一个排列,其中它的第一个元素的index
。
使用.splice()
的,.sort()
可以如果它们的用法,接着在图表的图案被包括;虽然在下面js
,他们实际上没有。
不完全肯定的是,下面js
的问题只是以下if (i % (total / len) === reset)
的声明,尽管这部分所需的时间最有投资;但仍没有返回预期结果。
具体地,现在参照该图表,在旋转时,例如向2
索引0
,1
索引2
。试图利用r
,这是一个负折射率,从右到横穿至左,以检索应该位于相邻“列”的index
0
下一个项目来实现这一点。
在下一栏,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?
这里有一个简单的解决方案来计算字符串的第n个排列:
function string_nth_permutation(str, n) {
var len = str.length, i, f, res;
for (f = i = 1; i <= len; i++)
f *= i;
if (n >= 0 && n < f) {
for (res = ""; len > 0; len--) {
f /= len;
i = Math.floor(n / f);
n %= f;
res += str.charAt(i);
str = str.substring(0, i) + str.substring(i + 1);
}
}
return res;
}
该算法遵循这些简单的步骤:
f = len!
元件的qazxsw POI总置换。factorial(len)
置换数量和选择的元素在结果偏移量。有迹象表明有任何给定的元素作为第一元素len
不同的排列。这种算法非常简单,有趣的性质:
(len-1)!
是在给定的顺序设定。(len-1)!
是最后一个:以相反的顺序设定0
。它可以很容易被转换为处理存储为一个阵列的一组:
factorial(a.length)-1
澄清:
a
首先计算为function array_nth_permutation(a, n) {
var b = a.slice(); // copy of the set
var len = a.length; // length of the set
var res; // return value, undefined
var i, f;
// compute f = factorial(len)
for (f = i = 1; i <= len; i++)
f *= i;
// if the permutation number is within range
if (n >= 0 && n < f) {
// start with the empty set, loop for len elements
for (res = []; len > 0; len--) {
// determine the next element:
// there are f/len subsets for each possible element,
f /= len;
// a simple division gives the leading element index
i = Math.floor(n / f);
// alternately: i = (n - n % f) / f;
res.push(b.splice(i, 1)[0]);
// reduce n for the remaining subset:
// compute the remainder of the above division
n %= f;
// extract the i-th element from b and push it at the end of res
}
}
// return the permutated set or undefined if n is out of range
return res;
}
。f
通过factorial(len)
的这个新值除以给出具有相同的初始元件f
时隙中的时隙号。 JavaScript没有整数分频,我们可以使用len
来划分的结果转换为它的组成部分,但它引入了一个限制组的12个元素。 n
允许套多达18元件。我们也可以使用f
,可能更有效了。len
必须降低到该插槽内的置换数,即除法(n / f) ... 0)
的其余部分。我们可以在第二循环中不同使用Math.floor(n / f)
,存储余数,避免(n - n % f) / f
和额外n
操作。下面是该环可能甚至更少可读的替代:
n / f
我想,这应该i
帮助你。该算法应该很容易翻译成JavaScript(我认为这是70%以上,已经JavaScript的兼容)。
Math.floor()
和%
是坏的电话,如果你是效率后使用。在后描述的算法是继最高效的执行next_permutation功能,即使集成在一些编程语言(如C ++例如)
编辑
当我遍历算法再次我觉得你可以只取出类型的变量,你要善于在JavaScript中去。
编辑
JavaScript版本:
// start with the empty set, loop for len elements
for (res = []; len > 0; len--) {
i = n % (f /= len);
res.push(b.splice((n - i) / f, 1)[0]);
n = i;
}
这可能是另一种解决方案,从slice
启发:
reverse
下面是我想到了我自己的方法的摘要,但自然也能找到它function nextPermutation(array) {
// Find non-increasing suffix
var i = array.length - 1;
while (i > 0 && array[i - 1] >= array[i])
i--;
if (i <= 0)
return false;
// Find successor to pivot
var j = array.length - 1;
while (array[j] <= array[i - 1])
j--;
var temp = array[i - 1];
array[i - 1] = array[j];
array[j] = temp;
// Reverse suffix
j = array.length - 1;
while (i < j) {
temp = array[i];
array[i] = array[j];
array[j] = temp;
i++;
j--;
}
return true;
}
:
function ps(a){
var res = [[]];
for (var i=0; i<a.length; i++){
while(res[res.length-1].length == i){
var l = res.pop();
for (var j=0; j<=l.length; j++){
var copy = l.slice();
copy.splice(j,0,a[i]);
res.unshift(copy);
}
}
}
return res;
}
console.log(JSON.stringify(ps(['a','b','c','d'])));
Steinhaus-Johnson-Trotter algorithm
说明
由于存在对于给定的阵列function p(input) {
var i, j, k, temp, base, current, outputs = [[input[0]]];
for (i = 1; i < input.length; i++) {
current = [];
for (j = 0; j < outputs.length; j++) {
base = outputs[j];
for (k = 0; k <= base.length; k++) {
temp = base.slice();
temp.splice(k, 0, input[i]);
current.push(temp);
}
}
outputs = current;
}
return outputs;
}
// call
var outputs = p(["a", "b", "c", "d"]);
for (var i = 0; i < outputs.length; i++) {
document.write(JSON.stringify(outputs[i]) + "<br />");
}
described elsewhere置换generatePermutations = function(arr) {
if (arr.length < 2) {
return arr.slice();
}
var factorial = [1];
for (var i = 1; i <= arr.length; i++) {
factorial.push(factorial[factorial.length - 1] * i);
}
var allPerms = [];
for (var permNumber = 0; permNumber < factorial[factorial.length - 1]; permNumber++) {
var unused = arr.slice();
var nextPerm = [];
while (unused.length) {
var nextIndex = Math.floor((permNumber % factorial[unused.length]) / factorial[unused.length - 1]);
nextPerm.push(unused[nextIndex]);
unused.splice(nextIndex, 1);
}
allPerms.push(nextPerm);
}
return allPerms;
};
和Enter comma-separated string (e.g. a,b,c):
<br/>
<input id="arrInput" type="text" />
<br/>
<button onclick="perms.innerHTML = generatePermutations(arrInput.value.split(',')).join('<br/>')">
Generate permutations
</button>
<br/>
<div id="perms"></div>
之间的每个数字编码的特定置换。要unencode置换数量,反复从factorial(arr.length)
直到没有剩下的元素删除元素。该元件以去除的精确指数由下式给定arr
。其它公式可以用于确定索引以除去,只要它保留数目和排列之间的一个一对一的映射。
例
下面是怎么回事排列将为阵列0
产生:
factorial(arr.length)-1
需要注意的是每个排列#的形式是:
arr
这基本上是在说明中给出的公式的逆过程。
我敢添加另一种答案,旨在回答您的问题有关(permNumber % factorial(arr.length)) / factorial(arr.length-1)
,(a,b,c,d)
,# Perm 1st El 2nd El 3rd El 4th El
0 abcd (a,b,c,d)[0] (b,c,d)[0] (c,d)[0] (d)[0]
1 abdc (a,b,c,d)[0] (b,c,d)[0] (c,d)[1] (c)[0]
2 acbd (a,b,c,d)[0] (b,c,d)[1] (b,d)[0] (d)[0]
3 acdb (a,b,c,d)[0] (b,c,d)[1] (b,d)[1] (b)[0]
4 adbc (a,b,c,d)[0] (b,c,d)[2] (b,c)[0] (c)[0]
5 adcb (a,b,c,d)[0] (b,c,d)[2] (b,c)[1] (b)[0]
6 bacd (a,b,c,d)[1] (a,c,d)[0] (c,d)[0] (d)[0]
7 badc (a,b,c,d)[1] (a,c,d)[0] (c,d)[1] (c)[0]
8 bcad (a,b,c,d)[1] (a,c,d)[1] (a,d)[0] (d)[0]
9 bcda (a,b,c,d)[1] (a,c,d)[1] (a,d)[1] (a)[0]
10 bdac (a,b,c,d)[1] (a,c,d)[2] (a,c)[0] (c)[0]
11 bdca (a,b,c,d)[1] (a,c,d)[2] (a,c)[1] (a)[0]
12 cabd (a,b,c,d)[2] (a,b,d)[0] (b,d)[0] (d)[0]
13 cadb (a,b,c,d)[2] (a,b,d)[0] (b,d)[1] (b)[0]
14 cbad (a,b,c,d)[2] (a,b,d)[1] (a,d)[0] (d)[0]
15 cbda (a,b,c,d)[2] (a,b,d)[1] (a,d)[1] (a)[0]
16 cdab (a,b,c,d)[2] (a,b,d)[2] (a,b)[0] (b)[0]
17 cdba (a,b,c,d)[2] (a,b,d)[2] (a,b)[1] (a)[0]
18 dabc (a,b,c,d)[3] (a,b,c)[0] (b,c)[0] (c)[0]
19 dacb (a,b,c,d)[3] (a,b,c)[0] (b,c)[1] (b)[0]
20 dbac (a,b,c,d)[3] (a,b,c)[1] (a,c)[0] (c)[0]
21 dbca (a,b,c,d)[3] (a,b,c)[1] (a,c)[1] (a)[0]
22 dcab (a,b,c,d)[3] (a,b,c)[2] (a,b)[0] (b)[0]
23 dcba (a,b,c,d)[3] (a,b,c)[2] (a,b)[1] (a)[0]
。
答案是有可能(几乎),但它不会是相当有效的。你在做什么你的算法如下:
(firstElIndex * 3!) + (secondElIndex * 2!) + (thirdElIndex * 1!) + (fourthElIndex * 0!)
和slice
其中concat
<reverse
和i
> j
,指数给定左到右)这主要是,我的第一个答案做什么,但在更多的最佳方式。
例
考虑置换9,10,11,8,7,6,5,4,3,2,1第一翻转从右到左是10,11,而真正的下一个排列是:9,11,1, 2,3,4,5,6,7,8,9,10 = 9concat(11)的concat(REV(8,7,6,5,4,3,2,1))的concat(10)
源代码在这里,我包括源代码,因为我想象它:
i
该代码是可以的jsfiddle qazxsw POI。
一个相当简单的C ++代码,而无需递归。
j
我们可以发现线性时间序列的下一个排列。
perm[i]
从@le_m。它可能会有所帮助。
下面的非常有效的算法使用
perm[j]
产生为O与运行的复杂N个元素的所有排列(N!):
var nextPermutation = function(arr) {
for (var i = arr.length - 2; i >= 0; i--) {
if (arr[i] < arr[i + 1]) {
return arr.slice(0, i).concat([arr[i + 1]]).concat(arr.slice(i + 2).reverse()).concat([arr[i]]);
}
}
// return again the first permutation if calling next permutation on last.
return arr.reverse();
}
console.log(nextPermutation([9, 10, 11, 8, 7, 6, 5, 4, 3, 2, 1]));
console.log(nextPermutation([6, 5, 4, 3, 2, 1]));
console.log(nextPermutation([1, 2, 3, 4, 5, 6]));