Using recursion and backtracking to generate all possible combinations

前端 未结 3 530
孤城傲影
孤城傲影 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:22

    A good way to think about forming N combinations is to look at the structure like a tree of combinations. Traversing that tree then becomes a natural way to think about the recursive nature of the algorithm you wish to implement, and how the recursive process would work.

    Let's say for instance that we have the sequence, {1, 2, 3, 4}, and we wish to find all the 3-combinations in that set. The "tree" of combinations would then look like the following:

                                  root
                            ________|___
                           |            | 
                         __1_____       2
                        |        |      |
                      __2__      3      3
                     |     |     |      |
                     3     4     4      4
    

    Traversing from the root using a pre-order traversal, and identifying a combination when we reach a leaf-node, we get the combinations:

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

    So basically the idea would be to sequence through an array using an index value, that for each stage of our recursion (which in this case would be the "levels" of the tree), increments into the array to obtain the value that would be included in the combination set. Also note that we only need to recurse N times. Therefore you would have some recursive function whose signature that would look something like the following:

    void recursive_comb(int step_val, int array_index, std::vector tuple);
    

    where the step_val indicates how far we have to recurse, the array_index value tells us where we're at in the set to start adding values to the tuple, and the tuple, once we're complete, will be an instance of a combination in the set.

    You would then need to call recursive_comb from another non-recursive function that basically "starts off" the recursive process by initializing the tuple vector and inputting the maximum recursive steps (i.e., the number of values we want in the tuple):

    void init_combinations()
    {
        std::vector tuple;
        tuple.reserve(tuple_size); //avoids needless allocations
        recursive_comb(tuple_size, 0, tuple);
    }
    

    Finally your recusive_comb function would something like the following:

    void recursive_comb(int step_val, int array_index, std::vector tuple)
    {
        if (step_val == 0)
        {
            all_combinations.push_back(tuple); //<==We have the final combination
            return;
        }
    
        for (int i = array_index; i < set.size(); i++)
        {
            tuple.push_back(set[i]);
            recursive_comb(step_val - 1, i + 1, tuple); //<== Recursive step
            tuple.pop_back(); //<== The "backtrack" step
        }
    
        return;
    }
    

    You can see a working example of this code here: http://ideone.com/78jkV

    Note that this is not the fastest version of the algorithm, in that we are taking some extra branches we don't need to take which create some needless copying and function calls, etc. ... but hopefully it gets across the general idea of recursion and backtracking, and how the two work together.

提交回复
热议问题