如何从一组元素中获取组合?
How to get combinations from set of elements?
我需要从一个数组中生成所有没有重复的组合,我阅读了一些相关内容,建议使用递归
我有一个数组
arr = [["A"], ["B"], ["C"], ["D"], ["E"], ["F"]]
我读到我可以使用像
这样的递归来解决这个问题
function combinations(arr, n, k)
//do something
//then
return combinations(arr, n, k)
在我的例子中 [A, B, C, D] 等同于 [A, B, D, C]。
我在 C++ 中找到了这个例子
http://www.martinbroadhurst.com/combinations.html
但是我无法复制它。
有什么建议可以解决这个问题吗?
PD:我正在使用 Python,但我对算法比语言更感兴趣。
[哎呀...当我发布答案时,C++
标签消失了]
[编辑了更多示例,包括使用 char]
代码中注释:
#include <vector>
// Function that recursively does the actual job
template <typename T, typename Function> void doCombinations(
size_t num, const std::vector<T>& values,
size_t start, std::vector<T>& combinationSoFar,
Function action
) {
if(0==num) { // the entire combination is complete
action(combinationSoFar);
}
else {
// walk through with the current position to the right,
// taking care to let enough walking room for the rest of the elements
for(size_t i=start; i<values.size()+1-num; i++) {
// push the current value there
combinationSoFar.push_back(values[i]);
// recursive call with one less element to enter combination
// and one position to the right for the next element to consider
doCombinations(num-1, values, i+1, combinationSoFar, action);
// pop the current value, we are going to move it to the right anyway
combinationSoFar.pop_back();
}
}
}
// function for the user to call. Prepares everything needed for the
// doCombinations
template <typename T, typename Function>
void for_each_combination(
size_t numInCombination,
const std::vector<T>& values,
Function action
) {
std::vector<T> combination;
doCombinations(numInCombination, values, 0, combination, action);
}
// dummy do-something with the vector
template <typename T> void cout_vector(const std::vector<T>& v) {
std::cout << '[';
for(size_t i=0; i<v.size(); i++) {
if(i) {
std::cout << ",";
}
std::cout << v[i];
}
std::cout << ']' << std::endl;
}
// Assumes the T type supports both addition and ostream <<
template <typename T> void adder(const std::vector<T>& vals) {
T sum=static_cast<T>(0);
for(T v : vals) {
sum+=v;
}
std::cout << "Sum: " << sum << " for ";
cout_vector(vals);
}
int main() {
std::cout << "Char combinations" << std::endl;
std::vector<char> char_vals{'A', 'B', 'C', 'D', 'E'};
for_each_combination(3, char_vals, cout_vector<char>);
std::cout << "\nInt combinations" << std::endl;
std::vector<int> int_vals{0, 1, 2, 3, 4};
for_each_combination(3, int_vals, cout_vector<int>);
std::cout <<"\nFloat combination adder" << std::endl;
std::vector<float> float_vals{0.0, 1.1, 2.2, 3.3, 4.4};
for_each_combination(3, float_vals, adder<float>);
return 0;
}
输出:
Char combinations
[A,B,C]
[A,B,D]
[A,B,E]
[A,C,D]
[A,C,E]
[A,D,E]
[B,C,D]
[B,C,E]
[B,D,E]
[C,D,E]
Int combinations
[0,1,2]
[0,1,3]
[0,1,4]
[0,2,3]
[0,2,4]
[0,3,4]
[1,2,3]
[1,2,4]
[1,3,4]
[2,3,4]
Float combination adder
Sum: 3.3 for [0,1.1,2.2]
Sum: 4.4 for [0,1.1,3.3]
Sum: 5.5 for [0,1.1,4.4]
Sum: 5.5 for [0,2.2,3.3]
Sum: 6.6 for [0,2.2,4.4]
Sum: 7.7 for [0,3.3,4.4]
Sum: 6.6 for [1.1,2.2,3.3]
Sum: 7.7 for [1.1,2.2,4.4]
Sum: 8.8 for [1.1,3.3,4.4]
Sum: 9.9 for [2.2,3.3,4.4]
对于任何组合数学问题,最好的编程方法是找出计数参数的递推关系。在组合的情况下,递推关系只是 C(n, k) = C(n - 1, k - 1) + C(n - 1, k)。
但这到底是什么意思呢?请注意,C(n - 1, k - 1) 意味着我们已经获取了数组的第一个元素,并且需要从其他 n - 1 个元素中再添加 k - 1 个元素。类似地,C(n - 1, k) 意味着我们不会选择数组的第一个元素作为 k 个元素之一。但请记住,如果 k 为 0,则 C(n, k) = 1,否则如果 n 为 0,则 C(n, k) = 0。在我们的问题中,k == 0 将 return 集合 包含空集,否则如果n == 0,我们将return空集。考虑到这一点,代码结构将如下所示:
def combinations(arr, k):
if k == 0:
return [[]]
elif len(arr) == 0:
return []
result = []
chosen = combinations(arr[1:], k - 1) #we choose the first element of arr as one of the k elements we need
notChosen = combinations(arr[1:], k) #first element not chosen in set of k elements
for combination in chosen:
result.append([arr[0]] + combination)
for combination in notChosen:
result.append(combination)
return result
现在,可以通过执行记忆来优化此功能(但这可以留给您作为练习,reader)。作为额外的练习,你能从它的计数关系开始勾勒出置换函数的样子吗?
提示:
P(n, k) = C(n, k)k! = [C(n - 1, k - 1) + C(n - 1, k)]k! = P(n - 1, k - 1)k + P(n - 1, k)
我需要从一个数组中生成所有没有重复的组合,我阅读了一些相关内容,建议使用递归
我有一个数组
arr = [["A"], ["B"], ["C"], ["D"], ["E"], ["F"]]
我读到我可以使用像
这样的递归来解决这个问题function combinations(arr, n, k)
//do something
//then
return combinations(arr, n, k)
在我的例子中 [A, B, C, D] 等同于 [A, B, D, C]。
我在 C++ 中找到了这个例子
http://www.martinbroadhurst.com/combinations.html
但是我无法复制它。
有什么建议可以解决这个问题吗?
PD:我正在使用 Python,但我对算法比语言更感兴趣。
[哎呀...当我发布答案时,C++
标签消失了]
[编辑了更多示例,包括使用 char]
代码中注释:
#include <vector>
// Function that recursively does the actual job
template <typename T, typename Function> void doCombinations(
size_t num, const std::vector<T>& values,
size_t start, std::vector<T>& combinationSoFar,
Function action
) {
if(0==num) { // the entire combination is complete
action(combinationSoFar);
}
else {
// walk through with the current position to the right,
// taking care to let enough walking room for the rest of the elements
for(size_t i=start; i<values.size()+1-num; i++) {
// push the current value there
combinationSoFar.push_back(values[i]);
// recursive call with one less element to enter combination
// and one position to the right for the next element to consider
doCombinations(num-1, values, i+1, combinationSoFar, action);
// pop the current value, we are going to move it to the right anyway
combinationSoFar.pop_back();
}
}
}
// function for the user to call. Prepares everything needed for the
// doCombinations
template <typename T, typename Function>
void for_each_combination(
size_t numInCombination,
const std::vector<T>& values,
Function action
) {
std::vector<T> combination;
doCombinations(numInCombination, values, 0, combination, action);
}
// dummy do-something with the vector
template <typename T> void cout_vector(const std::vector<T>& v) {
std::cout << '[';
for(size_t i=0; i<v.size(); i++) {
if(i) {
std::cout << ",";
}
std::cout << v[i];
}
std::cout << ']' << std::endl;
}
// Assumes the T type supports both addition and ostream <<
template <typename T> void adder(const std::vector<T>& vals) {
T sum=static_cast<T>(0);
for(T v : vals) {
sum+=v;
}
std::cout << "Sum: " << sum << " for ";
cout_vector(vals);
}
int main() {
std::cout << "Char combinations" << std::endl;
std::vector<char> char_vals{'A', 'B', 'C', 'D', 'E'};
for_each_combination(3, char_vals, cout_vector<char>);
std::cout << "\nInt combinations" << std::endl;
std::vector<int> int_vals{0, 1, 2, 3, 4};
for_each_combination(3, int_vals, cout_vector<int>);
std::cout <<"\nFloat combination adder" << std::endl;
std::vector<float> float_vals{0.0, 1.1, 2.2, 3.3, 4.4};
for_each_combination(3, float_vals, adder<float>);
return 0;
}
输出:
Char combinations
[A,B,C]
[A,B,D]
[A,B,E]
[A,C,D]
[A,C,E]
[A,D,E]
[B,C,D]
[B,C,E]
[B,D,E]
[C,D,E]
Int combinations
[0,1,2]
[0,1,3]
[0,1,4]
[0,2,3]
[0,2,4]
[0,3,4]
[1,2,3]
[1,2,4]
[1,3,4]
[2,3,4]
Float combination adder
Sum: 3.3 for [0,1.1,2.2]
Sum: 4.4 for [0,1.1,3.3]
Sum: 5.5 for [0,1.1,4.4]
Sum: 5.5 for [0,2.2,3.3]
Sum: 6.6 for [0,2.2,4.4]
Sum: 7.7 for [0,3.3,4.4]
Sum: 6.6 for [1.1,2.2,3.3]
Sum: 7.7 for [1.1,2.2,4.4]
Sum: 8.8 for [1.1,3.3,4.4]
Sum: 9.9 for [2.2,3.3,4.4]
对于任何组合数学问题,最好的编程方法是找出计数参数的递推关系。在组合的情况下,递推关系只是 C(n, k) = C(n - 1, k - 1) + C(n - 1, k)。 但这到底是什么意思呢?请注意,C(n - 1, k - 1) 意味着我们已经获取了数组的第一个元素,并且需要从其他 n - 1 个元素中再添加 k - 1 个元素。类似地,C(n - 1, k) 意味着我们不会选择数组的第一个元素作为 k 个元素之一。但请记住,如果 k 为 0,则 C(n, k) = 1,否则如果 n 为 0,则 C(n, k) = 0。在我们的问题中,k == 0 将 return 集合 包含空集,否则如果n == 0,我们将return空集。考虑到这一点,代码结构将如下所示:
def combinations(arr, k):
if k == 0:
return [[]]
elif len(arr) == 0:
return []
result = []
chosen = combinations(arr[1:], k - 1) #we choose the first element of arr as one of the k elements we need
notChosen = combinations(arr[1:], k) #first element not chosen in set of k elements
for combination in chosen:
result.append([arr[0]] + combination)
for combination in notChosen:
result.append(combination)
return result
现在,可以通过执行记忆来优化此功能(但这可以留给您作为练习,reader)。作为额外的练习,你能从它的计数关系开始勾勒出置换函数的样子吗?
提示: P(n, k) = C(n, k)k! = [C(n - 1, k - 1) + C(n - 1, k)]k! = P(n - 1, k - 1)k + P(n - 1, k)