["a", "b", "c", "d"]
将生成:[
"a", "b", "ab", "c", "ac",
"bc", "abc", "d", "ad", "bd",
"abd", "cd", "acd", "bcd", "abcd"
]
这是我编写的完成此任务的代码。
我想知道是否有更好的方法,因为两次遍历数组就像我在作弊一样,或者代码的复杂性在计算上比需要的要昂贵得多。 。
另外,一个函数的名称,该函数需要一个数组并返回组合,这叫什么?组合器似乎不合适。
var letters = ["a", "b", "c", "d"];
var combi = [];
var temp= "";
var letLen = Math.pow(2, letters.length);
for (var i = 0; i < letLen ; i++){
temp= "";
for (var j=0;j<letters.length;j++) {
if ((i & Math.pow(2,j))){
temp += letters[j]
}
}
if (temp !== "") {
combi.push(temp);
}
}
console.log(combi.join("\n"));
#1 楼
递归解决方案,最初在这里看到,但已进行了修改以满足您的要求(并且看起来有点JavaScript-y):/>
function combinations(str) {
var fn = function(active, rest, a) {
if (!active && !rest)
return;
if (!rest) {
a.push(active);
} else {
fn(active + rest[0], rest.slice(1), a);
fn(active, rest.slice(1), a);
}
return a;
}
return fn("", str, []);
}
输出:
combinations("abcd")
关于名称:不要命名
permutations
;排列是所有原始元素的排列(总共应为n!
)。换句话说,它已经具有精确的含义。不要不必要地使其过载。为什么不简单地将其命名为combinations
?#2 楼
您可以递归执行:function getCombinations(chars) {
var result = [];
var f = function(prefix, chars) {
for (var i = 0; i < chars.length; i++) {
result.push(prefix + chars[i]);
f(prefix + chars[i], chars.slice(i + 1));
}
}
f('', chars);
return result;
}
用法:
var combinations = getCombinations(["a", "b", "c", "d"]);
结果:
["a","ab","abc","abcd","abd","ac","acd","ad","b","bc","bcd","bd","c","cd","d"]
评论
\ $ \ begingroup \ $
...稍作调整,以返回数组集函数getCombinations(array){var result = []; var f = function(prefix = [],array){for(var i = 0; i
–mc。
19年6月3日在21:01
\ $ \ begingroup \ $
最好,最易读的解决方案!
\ $ \ endgroup \ $
– karthikaruna
19年8月17日在11:59
#3 楼
我更喜欢您的方法,而不是递归方法,尤其是在处理较大的列表时。一些注意事项: 200_success
如果您以
powerSet
开头,则无需检查combination.length !== 0
如果调用函数
i=1
,则不应调用构建的列表permutations
,这令人困惑您可以缓存
combinations
,这是常见的优化方法使用花括号,您可以拥有:
function powerSet( list ){
var set = [],
listSize = list.length,
combinationsCount = (1 << listSize),
combination;
for (var i = 1; i < combinationsCount ; i++ ){
var combination = [];
for (var j=0;j<listSize;j++){
if ((i & (1 << j))){
combination.push(list[j]);
}
}
set.push(combination);
}
return set;
}
没有它们,它看起来像this:
function powerSet( list ){
var set = [],
listSize = list.length,
combinationsCount = (1 << listSize);
for (var i = 1; i < combinationsCount ; i++ , set.push(combination) )
for (var j=0, combination = [];j<listSize;j++)
if ((i & (1 << j)))
combination.push(list[j]);
return set;
}
评论
\ $ \ begingroup \ $
第一个版本很棒。第二个则差强人意。
\ $ \ endgroup \ $
– 200_success
2014年1月21日在17:21
\ $ \ begingroup \ $
第二个版本太高尔夫了,但是我沉迷于掉冰壶。
\ $ \ endgroup \ $
– konijn
2014年1月21日17:39
\ $ \ begingroup \ $
我喜欢这个答案。如果我想改进(不需要),我会说:1.我将使用powerSet而不是set,或者至少使用一些不那么重载的含义。 2.我会避免使用第二个嵌套循环,因为大多数内部循环都没有使用。我认为回忆会很适合(jsfiddle.net/ynotravid/kkwfnpu7/20)。
\ $ \ endgroup \ $
–否
'18 Apr 10在23:30在
\ $ \ begingroup \ $
问题是,仅当它应该生成abcd,acbd,adbc ...等等时,才生成abcd,直到所有16个置换用尽并对3个字母和2个字母以及单字母字母进行相同的操作
\ $ \ endgroup \ $
– PirateApp
19年7月3日在12:33
#4 楼
另一种方法是先构建一个特里树,然后遍历该特里树以生成组合。有两个递归函数,并且我给它定的时间比您的迭代版本慢大约一个数量级,
但是我想您可能仍然觉得它很有趣。 (一旦JS获得
尾部调用优化,某些递归方法将运行得更快。)
var follows, combinations;
follows = function(a){
return a.map(function(item, i){
return [item, follows(a.slice(i+1))];
});
};
combinations = function(a){
var combs = function(prefix, trie, result){
trie.forEach(function(node, i){
result.push(prefix + node[0]);
combs(prefix + node[0], node[1], result);
});
return result;
};
return combs('', follows(a), []);
};
combinations(['a','b','c','d']);
PS。您的
permutations
函数输出数组数组,而不是问题顶部的示例数组。我已经使用我的combinations
函数输出了一个字符串数组。#5 楼
在这种情况下,非递归解决方案更容易理解,恕我直言: const powerset = (array) => { // O(2^n)
const results = [[]];
for (const value of array) {
const copy = [...results]; // See note below.
for (const prefix of copy) {
results.push(prefix.concat(value));
}
}
return results;
};
console.log(
powerset(['A', 'B', 'C'])
);
// [ [],
// [ 'A' ],
// [ 'B' ],
// [ 'A', 'B' ],
// [ 'C' ],
// [ 'A', 'C' ],
// [ 'B', 'C' ],
// [ 'A', 'B', 'C' ] ]
由于
results
在循环体内扩展,我们无法使用for-of
对其进行迭代-这样做也会对新添加的元素进行迭代,从而导致无限循环。我们只想在循环开始时迭代results
中的元素,即索引0
直到results.length - 1
。因此,我们要么将原始length
缓存在一个变量中,然后使用它,即 for (let index = 0, length = results.length; index < length; index++) {
const prefix = results[index];
// …
}
...…或者我们只是创建结果的静态副本并对其进行迭代。
#6 楼
如果x是整数,则执行Math.pow( 2, x )
的更快方法是1 << x
。该函数的好名字也可能是'array_permutator'。
评论
\ $ \ begingroup \ $
我的主要问题是使用if-checking进行这种双重循环。
\ $ \ endgroup \ $
–隐身
2011-12-19 20:33
\ $ \ begingroup \ $
是的,我知道。我无法提出消除嵌套循环的想法。因此,请等待,看看是否有人在想什么。
\ $ \ endgroup \ $
–迈克·纳基斯(Mike Nakis)
2011年12月19日20:39
#7 楼
我对此有两种解决方案,一种是二进制的,一种是递归的;二进制将如下;
function getSubArrays(arr){
var len = arr.length,
subs = Array(Math.pow(2,len)).fill();
return subs.map((_,i) => { var j = -1,
k = i,
res = [];
while (++j < len ) {
k & 1 && res.push(arr[j]);
k = k >> 1;
}
return res;
}).slice(1);
}
console.log(JSON.stringify(getSubArrays(["a","b","c","d"])));
递归如下:
function getSubArrays(arr){
if (arr.length === 1) return [arr];
else {
subarr = getSubArrays(arr.slice(1));
return subarr.concat(subarr.map(e => e.concat(arr[0])), [[arr[0]]]);
}
}
console.log(JSON.stringify(getSubArrays(["a","b","c","d"])));
#8 楼
在这一系列答案中,我认为我们缺少传统的递归解决方案,该解决方案强调了递归算法的优雅之处:难以理解:
const subSeq = (s) => {
if (s.length == 1) return ['', s]
// All the subSeq without the first char:
const ss = subSeq(s.slice(1))
// Return both those with and those without the first char
return [...ss.map(ch => s[0] + ch), ...ss]
}
答案中包含空字符串:
const subSeq = (s) => s.length == 1
? ['', s]
: (ss=subSeq(s.slice(1)),
[...ss.map(ch => s[0] + ch), ...ss])
#9 楼
试试这个简单的方法。var input="abcde";
var len=input.length;
var str = "";
var p=Math.pow(2,len);
var twoPower;
for(i = 0; i < p; i++)
{
twoPower=p;
for(j=0;j<len;j++)
{
twoPower=twoPower/2;
str+= (i & twoPower ? input.charAt(j) : "");
}
str+="\n";
}
alert(str);
它的工作方法是如此简单。
如果必须找到给定位长的所有二进制数,该怎么办?
是8 4 2 1方法。
如果位长为3,则可能的数字是
/>
4 2 1
0 0 0
0 0 1
0 1 0
0 1 1
1 0 0
1 0 1
1 1 0 0
1 1 1
这些是8个可能的值。
此处使用相同的方法,但1表示字符,不包含0。因此,可能的组合数量为(2 ^ n)-1。
评论
\ $ \ begingroup \ $
欢迎使用CodeReview。由于您的代码不过是代码转储而已,因此请提供上下文,以说明OP应该接受您的建议的原因/将其与他已经做的事情区分开的地方。
\ $ \ endgroup \ $
–莱加托
2015年4月1日在17:21
评论
\ $ \ begingroup \ $
跌跌撞撞回到这里,不知道为什么我最初没有指出“组合”也有一个现有的数学定义en.wikipedia.org/wiki/Combination
\ $ \ endgroup \ $
–韦恩
13年10月10日在6:41
\ $ \ begingroup \ $
“所有可能的组合”也可以称为“功率集”。
\ $ \ endgroup \ $
– 200_success
2014年1月2日在17:28
\ $ \ begingroup \ $
仔细检查后,动力装置应包括空弦。我不确定减去空字符串该怎么称呼电源。
\ $ \ endgroup \ $
– 200_success
2014年1月21日在17:24
\ $ \ begingroup \ $
我正在尝试创建我的babel库,这很方便xD
\ $ \ endgroup \ $
–凯尔
2015年10月1日,11:36
\ $ \ begingroup \ $
代替子序列(尽管从技术上讲,它也包括空序列),而不是“幂集”(约集,无需关心顺序),而是一个更好的名称。
\ $ \ endgroup \ $
–贝尔吉
16年7月31日在23:24