生成一个集合的所有子集,使它们的集合等于整个集合

Generating all subsets of a set, so that their reunion equals the whole set

本文关键字:集合 子集 于整个 一个      更新时间:2023-10-16

我有一个问题,我正试图在c++中实现。给定一组数字,我想生成所有的子集,使这些子集的集合等于整个集合。

例如,A ={1,2,3},我想得到:(1、2、3)
(1、2)(3)
(1、3)(2)
(1) (2,3)
(1) (2) (3)我得到这些子集的顺序并不重要。

我试图在c++/RCpp中做到这一点,因为在R中它往往是我拥有的代码的瓶颈。我现在的想法是有一个结构在X vector<vector<vector <int> > >中,我把所有的向量一个一个地加起来。循环遍历集合A

  • 为1,我添加{[(1)]}到X{1};
  • 我以这种方式添加下一个元素;假设我在X{1} =[(1)(2)]和X{2} =[(1,2)],下一个要加的数字是3。然后循环遍历X的元素。如果遇到只有一个向量的元素(如X{2}),则返回[(1,2)(3)]和[(1,2,3)]。如果我遇到一个有两个向量的元素(比如X{1}),那么我循环遍历这些向量,对于1我返回[(1,3)(2)],对于2我返回[(1),(2,3)],最后我加上[(1)(2),(3)]。

问题是,这个实现被证明比纯R中的类似实现要慢,后者已经相当慢了。

是否有一种稍微有效的方法来生成这些子集?也许可以先生成所有的子集(这在R中很容易做到),然后按这个顺序对它们进行分组(但我还没有弄清楚怎么做)。

编辑:c++代码(相当长)

std::vector<std::vector < int> > push_one(std::vector<int> x, int add) {
  std::vector< std::vector <int> > res;
  res.push_back(x);
  std::vector<int> newvec;
  newvec.push_back(add);
  res.push_back(newvec);
  return res;
}

std::vector<std::vector<std::vector <int> > > push_two(std::vector<int> x, int add) {
  std::vector<std::vector <int> > newvec;
  std::vector< std::vector< std::vector<int> > > res;
  res.push_back(push_one(x, add));
  x.push_back(add);
  newvec.push_back(x);
  res.push_back(newvec);

  return res;
}

// push_one_rest does the same as push_one, just that it takes a 'rest' argument, that adds another (vector of vectors) at the end.
std::vector<std::vector < int> > push_one_rest(std::vector<int> x, int add, std::vector<std::vector<int> > rest) {
  std::vector< std::vector <int> > res;
  res.push_back(x);
  std::vector<int> newvec;
  newvec.push_back(add);
  res.push_back(newvec);
  res.insert(res.end(), rest.begin(), rest.end());
  return res;
}
//push_two_rest does the same as push_two but it takes the rest argument adding at the end of every element.
std::vector<std::vector<std::vector <int> > > push_two_rest(std::vector<int> x, int add, std::vector<std::vector<int> > rest) {
  std::vector<std::vector <int> > newvec;
  std::vector< std::vector< std::vector<int> > > res;

  //res.push_back(push_one_rest(x, add, rest));

  x.push_back(add);
  newvec.push_back(x);
  newvec.insert(newvec.end(), rest.begin(), rest.end());
  res.push_back(newvec);

  return res;
}

// additref adds a new number.
void additref(std::vector<std::vector<std::vector<int> > > &x, int add) {
  int xsize = x.size();
  for(int i = 0; i<xsize; i++) {
    std::vector<std::vector<int> > herevec = x[i];
    if(herevec.size()==1) {
      std::vector<int> actualvec = herevec[0];
      std::vector<std::vector<std::vector <int> > > newvec = push_two(actualvec, add);
      x.insert(x.end(), newvec.begin(), newvec.end() );
    }
    if(herevec.size()>1) {
      std::vector<int> addvec;
      addvec.push_back(add);
      for(int i=0; i<herevec.size(); i++) {
        // put the rest into rest
        std::vector<int> basis = herevec[i];
        std::vector<std::vector<int> > rest = herevec;  
        rest.erase (rest.begin()+i);

        std::vector<std::vector<std::vector <int> > > newvec2 = push_two_rest(basis, add, rest);

        x.insert(x.end(),newvec2.begin(), newvec2.end() );
      }
      herevec.push_back(addvec);
      x.push_back(herevec);
    }
  }
  x.erase (x.begin(),x.begin()+xsize);
}

// The function which adds the numbers one after another.
std::vector<std::vector<std::vector <int> > > divide(std::vector<int> division) {
  std::vector<std::vector<std::vector <int> > > res;
  std::vector<int> x;
  x.push_back(division[0]);
  std::vector<std::vector<int> > x2;
  x2.push_back(x);
  res.push_back(x2);

  for(std::vector<int>::iterator it4 = division.begin()+1; it4<division.end(); it4++) {
    additref(res, *it4);
  }
  return res;
}

我认为你最好使用bitset。给定一个包含N个元素的集合S,你可以将S中的每个元素映射到一个位,然后通过包含与'1'对应的元素来生成该子集。

例如给定S = {A, B, C}你得到

000 -> {}
001 -> {C}
010 -> {B}
011 -> {B, C}
100 -> {A}
101 -> {A, C}
110 -> {A, B}
111 -> {A, B, C}

来自"partitions"包的listParts函数似乎可以工作:

> require(partitions)
> listParts(4)
[[1]]
[1] (1,2,3,4)
[[2]]
[1] (1,2,4)(3)
[[3]]
[1] (1,2,3)(4)
[[4]]
[1] (1,3,4)(2)
[[5]]
[1] (2,3,4)(1)
[[6]]
[1] (1,4)(2,3)
[[7]]
[1] (1,2)(3,4)
[[8]]
[1] (1,3)(2,4)
[[9]]
[1] (1,4)(2)(3)
[[10]]
[1] (1,2)(3)(4)
[[11]]
[1] (1,3)(2)(4)
[[12]]
[1] (2,4)(1)(3)
[[13]]
[1] (2,3)(1)(4)
[[14]]
[1] (3,4)(1)(2)
[[15]]
[1] (1)(2)(3)(4)
>