Generating all size k subsets of {0, 1, 2, … n-1}

匿名 (未验证) 提交于 2019-12-03 08:54:24

问题:

I want to generate all cardinality k subsets of {0, 1, 2, ..., n-1} in C++. In Haskell, I would do:

sets 0 n = [[]] sets k n = [i:s | i <- [0..n-1], s <- sets (k-1) i] 

Or in Python:

def sets(k, n):     if k == 0:         return [()]     return ((i,)+s for i in range(n) for s in sets(k-1, i)) 

So, for example, (line breaks added for clarity)

ghci> sets 2 8 [[1,0],  [2,0],[2,1],  [3,0],[3,1],[3,2],  [4,0],[4,1],[4,2],[4,3],  [5,0],[5,1],[5,2],[5,3],[5,4],  [6,0],[6,1],[6,2],[6,3],[6,4],[6,5],  [7,0],[7,1],[7,2],[7,3],[7,4],[7,5],[7,6]] 

What would be the "C++ way" of doing this? Note that I'm not asking how to solve the problem. I'm asking about what data types would be considered "normal" by C++ programmers.

(For reference, I'm vaguely familiar with C++ and somewhat familiar with C.)

回答1:

Here's a naive, recursive approach, which implements the classical combinatorial identity:

binom(n + 1, k + 1) = binom(n, k + 1) + binom(n, k) 


#include <set>  typedef std::set<int> intset;  std::set<intset> subsets(std::size_t k, intset s) {     if (k == 0 || s.empty() || s.size() < k) { return { { } }; }      if (s.size() == k) { return { s }; }      auto x = *s.begin();     s.erase(s.begin());      std::set<intset> result;      for (auto & t : subsets(k - 1, s))     {         auto r = std::move(t);         r.insert(x);         result.insert(std::move(r));     }      for (auto & t : subsets(k, s))     {         results.insert(std::move(t));     }      return result; } 

Usage:

auto ss = subsets(3, {0, 1, 2, 3, 4}); 

Complete example:

#include <iostream> #include <string> #include <prettyprint.hpp>  int main(int argc, char * argv[]) {     if (argc != 3) return 1;      auto k = std::stoul(argv[1]);     auto n = std::stoul(argv[2]);      intset s;     for (auto i = 0U; i != n; ++i) s.insert(i);      std::cout << subsets(k, s) << std::endl; } 


回答2:

Rosetta Code has an implementation that works by taking the first k entries of permutations of the list 0, 1, ..., n-1. It uses the C++ STL.



回答3:

The concept of a set of all subsets is called the power set, and Wikipedia has quite a bit written on it. One section is even dedicated to algorithms for doing what you want. This particular problem requests the subsets of limited cardinality of the power set. You should probably use std::set.



回答4:

A quick implementation (using recursion) of this in C would be the following:

#include <stdio.h>  #define N 8 #define K 3  void print_combination(int* combination, int k) {     int i;     for (i = 0; i < k; i++){         printf("%d ", combination[i]);     }     printf("\n"); }  void find_all_combinations(int idx, int* in_use, int* combination,         int n, int k) {     int i;     if (idx == k){         print_combination(combination, k);         return;     }      for (i = 0; i < n; i++){         if (in_use[i]){             continue;         }          in_use[i] = 1;         combination[idx++] = i + 1;          find_all_combinations(idx, in_use, combination, n, k);          combination[--idx] = 0;         in_use[i] = 0;     } }  int main(void) {     /* Ensure that the arrays are initialized with zeroes. */     int in_use[N] = {0};     int curr_combination[K] = {0};     find_all_combinations(0, in_use, curr_combination, N, K);      return 0; } 


标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!