获取所有可能集合的算法[重复]
Posted
技术标签:
【中文标题】获取所有可能集合的算法[重复]【英文标题】:Algorithm to get all possible sets [duplicate] 【发布时间】:2013-12-01 07:41:43 【问题描述】:输入:
[ [a1,b1,c1], [a2,b2,c2,d,2], [a3,b3], ...]
输出:
[ [a1,a2,a3], [a1,a2,b3], [a1,b2,a3], [a1,b2,b3], [a1,c2,a3], [a1,c2,b3], ... ]
所以我需要组合所有可能的集合(顺序无关紧要)。每个输出集nth
成员都是nth
输入集的成员。我需要高效的算法,最好是在 javascript 中。
编辑
我正在努力解决这个问题。
var input = [ [a,b,c], [a1,b1,c1], [a2,b2] ];
var combinationsNum = _.reduce(input,function(num,set) return num*set.length; , 1);
var output = new Array(combinationsNum);
for(var i = 0; i < output.length; ++i) output[i] = [];
for(var s = 0; s < input.length; ++s)
var set = input[s];
for(var m = 0; m < set.length; ++m)
var memeber = set[m];
// now I need to calculate to which output arrays I have to push this member
// result should be
// a a1 a2
// a b1 b2
// a c1 a2
// a a1 b2
// a b1 a2
// a c1 b2
// b a1 a2
// b b1 b2
// b c1 a2
// b a1 b2
// b b1 a2
// b c1 b2
// c a1 a2
// c b1 b2
// c c1 a2
// c a1 b2
// c b1 a2
// c c1 b2
正如您在每个 set
上看到的那样,我必须以一定的间隔和时间将其每个成员推送到每个输出数组...我有计算这个问题...
我在这个重复的问题中找到的最快的方法是:
function(arg)
var r = [], max = arg.length-1;
function helper(arr, i)
for (var j=0, l=arg[i].length; j<l; j++)
var a = arr.slice(0); // clone arr
a.push(arg[i][j])
if (i==max)
r.push(a);
else
helper(a, i+1);
helper([], 0);
return r;
;
【问题讨论】:
请展示你的尝试。 【参考方案1】:实现此目的的一种可能方法是使用递归。我对 JavaScript 不熟悉,以下是 C++ 版本。你可以相应地调整它。
#include <iostream>
#include <vector>
using namespace std;
vector< vector<int> > input;
void recurCombination( vector<int>& outputSoFar )
// base case: If number of entries in our output = size of input array, then print it.
if ( outputSoFar.size() == input.size() )
// print outputSoFar
return;
// else take next subarray in the input array.
int sizeSofar = outputSoFar.size();
// for each element in that subarray, choose an element and recur further.
for ( int i = 0; i < input[sizeSoFar].size(); i++ )
vector<int> newSoFar( outputSoFar );
newSoFar.push_back( input[sizeSoFar][i] );
recurCombination( newSoFar )
int main()
// read input vector
vector<int> emptySoFar;
recurCombination( emptySoFar );
return 0;
【讨论】:
【参考方案2】:由于您在输出中拥有的元素数量为 length1*length2*...lengthN
,因此一种简单地迭代每个数组并构建输出的算法是您可以获得的最有效的算法。
function buildAll(arr)
var i;
// count the total number of arrays in the output
var count = 1;
for (i=0; i<arr.length; i++)
count *= arr[i].length; // fails if empty arrays are in input, make sure you extract those
// prepare the output arrays
var output = [];
for (i=0; i<count; i++)
output.push([]);
// fill the arrays
var total = count;
for (i=0; i<arr.length; i++)
count /= arr[i].length;
for (var k=0; k<total; k++)
output[k].push(arr[i][Math.floor((k/count)%arr[i].length)]);
return output;
// usage
var input = [ ['a1','b1','c1'], ['a2','b2','c2','d2'], ['a3','b3']];
var output = buildAll(input);
console.log(output);
演示:http://jsbin.com/UfAnIMus/1/edit
【讨论】:
【参考方案3】:function concat(a, b)
return a.concat(b);
function combinations(a)
return a.length == 0 ? [[]] : a[0].map(function(x)
return combinations(a.slice(1)).map(concat.bind(null, [x]));
).reduce(concat, []);
无法抗拒;)
编辑:
这里是原代码的固定版本。
var lengths = input.map(function(a) return a.length; );
function product(arr) return arr.reduce(function(a, b) return a * b; , 1);
var numCombinations = product(lengths);
var output = new Array(numCombinations);
for(var i = 0; i < output.length; ++i) output[i] = [];
for(var s = 0; s < input.length; ++s)
var set = input[s];
var runLength = product(lengths.slice(s + 1));
var j = 0;
while (j < numCombinations)
for (var m = 0; m < set.length; ++m)
for (var i = 0; i < runLength; i++)
output[j][s] = set[m];
j++;
解释:
任何给定数组位置的模式都比较简单。例如,考虑集合 [ a1, b1, c1, a2, b2, c2, a3, b3 ] 的可能组合:
[a1, a2, a3][a1, a2, b3][a1, b2, a3] >[a1, b2, b3][a1, c2, a3][a1, c2, b3] em>[b1, a2, a3][b1, a2, b3][b1, b2, a3] ...
第二列中的元素循环遍历值 a2, b2, c2,每个元素重复两次。一般来说,重复次数等于剩余组的组合数(这是它们大小的乘积)。第二列之后唯一剩下的集合是 a3, b3,它的大小为 2,因此每个元素出现两次。对于第一列,剩余的集合是 a2, b2, c2 和 a3, b3 所以它们的大小的乘积是 3 × 2 = 6,即这就是 a1 出现 6 次的原因。
【讨论】:
以上是关于获取所有可能集合的算法[重复]的主要内容,如果未能解决你的问题,请参考以下文章