如何查找给定数组的所有可能子集?

时间:2009-03-24 20:54:06

标签: c# c++ algorithm

我想在C#或C ++中提取数组的所有可能子集,然后计算所有子集数组各自元素的总和,以检查它们中有多少等于给定数字。

我正在寻找的是算法。我确实理解这里的逻辑,但我现在还没能实现这个。

4 个答案:

答案 0 :(得分:12)

考虑SN元素集和给定子集,每个元素都属于或不属于该子集。因此,2^N个可能的子集(如果包含原始集和空集),并且x和{{1}之间的0二进制表示中的位直接映射} 2^N x的{​​{1}}子集中的元素。

一旦你弄清楚如何枚举给定子集的元素,添加值很简单。

为了找到大S的总t的子集,一个优化可能是记录那些超过N的子集,而不是测试那些是那些的正确超集的子集。测试集合号t是否是集合x的超集可以通过单个按位运算和整数比较来实现。

答案 1 :(得分:7)

您正在寻找的是power setRosetta Code有很多不同的实现,但这里是他们的C ++代码(他们使用向量而不是数组,但它应该很容易翻译)。

#include <iostream>
#include <set>
#include <vector>
#include <iterator>

typedef std::set<int> set_type;
typedef std::set<set_type> powerset_type;

powerset_type powerset(set_type const& set)
{
  typedef set_type::const_iterator set_iter;
  typedef std::vector<set_iter> vec;
  typedef vec::iterator vec_iter;

  struct local
  {
    static int dereference(set_iter v) { return *v; }
  };

  powerset_type result;

  vec elements;
  do
  {
    set_type tmp;
    std::transform(elements.begin(), elements.end(),
                   std::inserter(tmp, tmp.end()),
                   local::dereference);
    result.insert(tmp);
    if (!elements.empty() && ++elements.back() == set.end())
    {
      elements.pop_back();
    }
    else
    {
      set_iter iter;
      if (elements.empty())
      {
        iter = set.begin();
      }
      else
      {
        iter = elements.back();
        ++iter;
      }
      for (; iter != set.end(); ++iter)
      {
        elements.push_back(iter);
      }
    }
  } while (!elements.empty());

  return result;
}

int main()
{
  int values[4] = { 2, 3, 5, 7 };
  set_type test_set(values, values+4);

  powerset_type test_powerset = powerset(test_set);

  for (powerset_type::iterator iter = test_powerset.begin();
       iter != test_powerset.end();
       ++iter)
  {
    std::cout << "{ ";
    char const* prefix = "";
    for (set_type::iterator iter2 = iter->begin();
         iter2 != iter->end();
         ++iter2)
    {
      std::cout << prefix << *iter2;
      prefix = ", ";
    }
    std::cout << " }\n";
  }
}

输出:

{  }
{ 2 }
{ 2, 3 }
{ 2, 3, 5 }
{ 2, 3, 5, 7 }
{ 2, 3, 7 }
{ 2, 5 }
{ 2, 5, 7 }
{ 2, 7 }
{ 3 }
{ 3, 5 }
{ 3, 5, 7 }
{ 3, 7 }
{ 5 }
{ 5, 7 }
{ 7 }

答案 2 :(得分:5)

这是我4/5年前的大学项目之一,我无法很好地提醒算法。正如我所看到的那样我的记忆是使用数组作为原始集合和位掩码来指示当前子集中存在哪些元素 这是来自档案馆的未经测试的代码:

#include <iostream>

#ifndef H_SUBSET
#define H_SUBSET

template <class T>
class Subset {
    private:
        int Dimension;
        T *Set;
        bool *Bitmask;
    public:
        Subset(T *set, int dim);
        ~Subset(void);
        void Show(void);
        void NextSubset(void);
        void EmptySet(void);
        void FullSet(void);
        int SubsetCardinality(void);
        int SetCardinality(void);
        T operator[](int index);
};      

template <class T>
int Subset<T>::SetCardinality(void) {
    return Dimension;
}

template <class T>
int Subset<T>::SubsetCardinality(void) {
    int dim = 0;
    for(int i = 0; i<Dimension; i++) {
        if(Bitmask[i]) {
            dim++;
        }
    }
    return dim;
}

template <class T>
void Subset<T>::EmptySet(void) {
    for(int i = 0; i<Dimension; i++) {
        Bitmask[i] = 0;
    }
    return;
}

template <class T>
void Subset<T>::FullSet(void) {
    for(int i = 0; i<Dimension; i++) {
        Bitmask[i] = 1;
    }
    return;
}

template <class T>
void Subset<T>::NextSubset(void) {
    int i = Dimension - 1;
    while(!Bitmask[i]&&i>=0) {
        i--;
        if(i<0) {
            break;
        }
    }
    if(i>=0) {
        Bitmask[i] = !Bitmask[i];
    }
    for(int j = i+1; j < Dimension; j++) {
        Bitmask[j] = !Bitmask[j];
    }
    return;
}

template <class T>
void Subset<T>::Show(void) {
    std::cout << "{ ";
    for(int i=0; i<Dimension; i++) {
        if(Bitmask[i]) {
            std::cout << Set[i] << ", ";
        }
    }
    std::cout << "}";
    return;
}

template <class T>
Subset<T>::Subset(T *set, int dim) {
    Set = new T[dim];
    Bitmask = new bool[dim];
    Dimension = dim;
    for(int i=0; i<dim; i++) {
        Set[i] = set[i];
        Bitmask[i] = true;
    }
}

template <class T>
Subset<T>::~Subset(void) {
    delete [] Set;
    delete [] Bitmask;
}
#endif // H_SUBSET

如果这是你的家庭作业,你就是自杀了兄弟;)

答案 3 :(得分:1)

你呢;

A)真的想找到所有可能的子集吗?

B)希望找到阵列中可以组合多少元素以等于给定数量,并将A)视为解决方案的一步?

如果它是A)那么它非常简单,但可能的子集数量很快变得非常大。

如果是B)那么你应该先对数组进行排序并从那里开始工作。