如何从元素集中获得组合?

时间:2016-10-31 05:20:36

标签: combinations permutation combinatorics

我需要生成所有组合而不重复数组,我读了一些关于它的建议使用递归

我有一个数组

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,但我对算法比对语言更感兴趣。

2 个答案:

答案 0 :(得分:1)

[哎呀...当我发布答案时,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]

答案 1 :(得分:1)

对于任何组合学问题,编程它的最佳方法是计算计数参数的递归关系。在组合的情况下,递归关系简单地是C(n,k)= C(n-1,k-1)+ C(n-1,k)。 但这究竟意味着什么呢?注意,C(n - 1,k - 1)意味着我们已经取得了数组的第一个元素,并且需要k - 1个来自其他n - 1个元素的元素。类似地,C(n - 1,k)意味着我们不会选择数组的第一个元素作为k个元素之一。但请记住,如果k为0,则C(n,k)= 1,否则如果n为0则C(n,k)= 0.在我们的问题中,k == 0将返回一个包含 / em>空集,否则如果n == 0,我们将返回空集。考虑到这一点,代码结构如下所示:

>>> a
'wswwswwwswwwws'
>>> b
['ws', 'wws']

现在,可以通过执行记忆来优化此功能(但这可以作为练习留给您,读者)。作为一个额外的练习,你能否勾勒出排列函数从计数关系开始的样子?

提示: 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)