Using recursion and backtracking to generate all possible combinations

前端 未结 3 528
孤城傲影
孤城傲影 2020-12-14 05:02

I\'m trying to implement a class that will generate all possible unordered n-tuples or combinations given a number of elements and the size of the combination.

In ot

3条回答
  •  借酒劲吻你
    2020-12-14 05:26

    Personally I would go with a simple iterative solution.

    Represent you set of nodes as a set of bits. If you need 5 nodes then have 5 bits, each bit representing a specific node. If you want 3 of these in your tupple then you just need to set 3 of the bits and track their location.

    Basically this is a simple variation on fonding all different subsets of nodes combinations. Where the classic implementation is represent the set of nodes as an integer. Each bit in the integer represents a node. The empty set is then 0. Then you just increment the integer each new value is a new set of nodes (the bit pattern representing the set of nodes). Just in this variation you make sure that there are always 3 nodes on.

    Just to help me think I start with the 3 top nodes active { 4, 3, 2 }. Then I count down. But it would be trivial to modify this to count in the other direction.

    #include 
    #include 
    
    
    class TuppleSet
    {
        friend std::ostream& operator<<(std::ostream& stream, TuppleSet const& data);
    
        boost::dynamic_bitset<> data;    // represents all the different nodes
        std::vector        bitpos;  // tracks the 'n' active nodes in the tupple
    
        public:
            TuppleSet(int nodes, int activeNodes)
                : data(nodes)
                , bitpos(activeNodes)
            {
                // Set up the active nodes as the top 'activeNodes' node positions.
                for(int loop = 0;loop < activeNodes;++loop)
                {
                    bitpos[loop]        = nodes-1-loop;
                    data[bitpos[loop]]  = 1;
                }
            }
            bool next()
            {
                // Move to the next combination
                int bottom  = shiftBits(bitpos.size()-1, 0);
                // If it worked return true (otherwise false)
                return bottom >= 0;
            }
        private:
            // index is the bit we are moving. (index into bitpos)
            // clearance is the number of bits below it we need to compensate for.
            //
            //  [ 0, 1, 1, 1, 0 ]   =>    { 3, 2, 1 }
            //             ^
            //             The bottom bit is move down 1 (index => 2, clearance => 0)
            //  [ 0, 1, 1, 0, 1]    =>    { 3, 2, 0 }
            //                ^
            //             The bottom bit is moved down 1 (index => 2, clearance => 0)
            //             This falls of the end
            //          ^
            //             So we move the next bit down one (index => 1, clearance => 1)
            //  [ 0, 1, 0, 1, 1]
            //                ^
            //             The bottom bit is moved down 1 (index => 2, clearance => 0)
            //             This falls of the end
            //             ^
            //             So we move the next bit down one (index =>1, clearance => 1)
            //             This does not have enough clearance to move down (as the bottom bit would fall off)
            //      ^      So we move the next bit down one (index => 0, clearance => 2)
            // [ 0, 0, 1, 1, 1] 
            int shiftBits(int index, int clerance)
            {
                if (index == -1)
                {   return -1;
                }
                if (bitpos[index] > clerance)
                {
                    --bitpos[index];
                }
                else
                {
                    int nextBit = shiftBits(index-1, clerance+1);
                    bitpos[index] = nextBit-1;
                }
                return bitpos[index];
            }
    };
    
    std::ostream& operator<<(std::ostream& stream, TuppleSet const& data)
    {
        stream << "{ ";
        std::vector::const_iterator loop = data.bitpos.begin();
        if (loop != data.bitpos.end())
        {
            stream << *loop;
            ++loop;
            for(; loop != data.bitpos.end(); ++loop)
            {
                stream << ", " << *loop;
            }
        }
        stream << " }";
        return stream;
    }
    

    Main is trivial:

    int main()
    {
        TuppleSet   s(5,3);
    
        do
        {
            std::cout << s << "\n";
        }
        while(s.next());
    }
    

    Output is:

    { 4, 3, 2 }
    { 4, 3, 1 }
    { 4, 3, 0 }
    { 4, 2, 1 }
    { 4, 2, 0 }
    { 4, 1, 0 }
    { 3, 2, 1 }
    { 3, 2, 0 }
    { 3, 1, 0 }
    { 2, 1, 0 }
    

    A version of shiftBits() using a loop

        int shiftBits()
        {
            int bottom   = -1;
            for(int loop = 0;loop < bitpos.size();++loop)
            {
                int index   = bitpos.size() - 1 - loop;
                if (bitpos[index] > loop)
                {
                    bottom = --bitpos[index];
                    for(int shuffle = loop-1; shuffle >= 0; --shuffle)
                    {
                        int index   = bitpos.size() - 1 - shuffle;
                        bottom = bitpos[index] = bitpos[index-1]  - 1;
                    }
                    break;
                }
            }
            return bottom;
        }
    

提交回复
热议问题