Search code examples
c++setpowerset

Iteratively calculate the power set of a set or vector


While there are plenty of examples on how to generate the actual power set of a set, I can't find anything about iteratively (as in std::iterator) generating the power set. The reason why I would appreciate such an algorithm is the size of my base set. As the power set of a n-element set has 2^n elements, I would quickly run out of memory when actually computing the set. So, is there any way to create an iterator for the power set of a given set? Is it even possible?

  • If it would be easier, an iterator that creates sets of ints would be fine - I could use them as indices for the actual set/vector.
  • As I actually work on a std::vector, random access would be possible if neccessary

Solution

  • Using for_each_combination from Combinations and Permutations one can easily iterate through all members of the power set of a std::vector<AnyType>. For example:

    #include <vector>
    #include <iostream>
    #include "../combinations/combinations"
    
    int
    main()
    {
        std::vector<int> v{1, 2, 3, 4, 5};
        std::size_t num_visits = 0;
        for (std::size_t k = 0; k <= v.size(); ++k)
            for_each_combination(v.begin(), v.begin()+k, v.end(),
                [&](auto first, auto last)
                {
                    std::cout << '{';
                    if (first != last)
                    {
                        std::cout << *first;
                        for (++first; first != last; ++first)
                            std::cout << ", " << *first;
                    }
                    std::cout << "}\n";
                    ++num_visits;
                    return false;
                });
        std::cout << "num_visits = " << num_visits << '\n';
    }
    

    This visits each power set member of this vector, and executes the functor, which simply counts the number of visits and prints out the current power set:

    {}
    {1}
    {2}
    {3}
    {4}
    {5}
    {1, 2}
    {1, 3}
    {1, 4}
    {1, 5}
    {2, 3}
    {2, 4}
    {2, 5}
    {3, 4}
    {3, 5}
    {4, 5}
    {1, 2, 3}
    {1, 2, 4}
    {1, 2, 5}
    {1, 3, 4}
    {1, 3, 5}
    {1, 4, 5}
    {2, 3, 4}
    {2, 3, 5}
    {2, 4, 5}
    {3, 4, 5}
    {1, 2, 3, 4}
    {1, 2, 3, 5}
    {1, 2, 4, 5}
    {1, 3, 4, 5}
    {2, 3, 4, 5}
    {1, 2, 3, 4, 5}
    num_visits = 32
    

    The syntax I've used above is C++14. If you have C++11, you will need to change:

    [&](auto first, auto last)
    

    to:

    [&](std::vector<int>::const_iterator first, std::vector<int>::const_iterator last)
    

    And if you are in C++98/03, you will have to write a functor or function to replace the lambda.

    The for_each_combination function allocates no extra storage. This is all done by swapping members of the vector into the range [v.begin(), v.begin()+k). At the end of each call to for_each_combination the vector is left in its original state.

    If for some reason you want to "exit" the for_each_combination early, simply return true instead of false.