Recursively generate all combinations of a given subset size (C ++)

Observe the following code:

#include <vector>
#include <iostream>
#include <string>

template <typename T>
void print_2d_vector(std::vector<std::vector<T>>& v)
{
  for(int i = 0; i < v.size(); i++)
  {
    std::cout << "{";
    for(int j = 0; j < v[i].size(); j++)
    {
      std::cout << v[i][j];
      if(j != v[i].size() - 1)
      {
        std::cout << ", ";
      }
    }
    std::cout << "}\n";
  }
}

template <typename T>
struct permcomb2
{
  std::vector<std::vector<T>> end_set;
  std::vector<T>* data;
  permcomb2(std::vector<T>& param) : data(&param) {}

  void helpfunc(std::vector<T>& seen, int depth)
  {
    if(depth == 0)
    {
      end_set.push_back(seen);
    }
    else
    {
      for(int i = 0; i < (*data).size(); i++)
      {
        seen.push_back((*data)[i]);
        helpfunc(seen, depth - 1);
        seen.pop_back();
      }
    }
  }
};

template <typename T>
std::vector<std::vector<T>> permtest(std::vector<T>& data, int subset_size)
{
  permcomb2<T> helpstruct(data);
  std::vector<T> empty {};
  helpstruct.helpfunc(empty, subset_size);
  return helpstruct.end_set;
}

using namespace std;
int main()
{
  std::vector<std::string> flavors {"Vanilla", "Chocolate", "Strawberry"};
  auto a1 = permtest(flavors, 2);

  cout << "Return all combinations with repetition\n";
  print_2d_vector(a1);
  return 0;
}

Running this code leads to the following output:

Return all combinations with repetition
{Vanilla, Vanilla}
{Vanilla, Chocolate}
{Vanilla, Strawberry}
{Chocolate, Vanilla}
{Chocolate, Chocolate}
{Chocolate, Strawberry}
{Strawberry, Vanilla}
{Strawberry, Chocolate}
{Strawberry, Strawberry}

Notice how this code DOES NOT what it claims! Instead of returning all combinations repeating the given size of the subset (target), it instead returns all permutations repeating the given size of the subset. Of course, a way to get combinations would be to generate all the permutations, as I did, and then scroll to delete all but one of them, which are each other's permutations. But I am sure that this is absolutely NOT the most effective way to do this.

, , . , . , , "helpfunc", .

, :

Return all combinations with repetition
{Vanilla, Vanilla}
{Vanilla, Chocolate}
{Vanilla, Strawberry}
{Chocolate, Chocolate}
{Chocolate, Strawberry}
{Strawberry, Strawberry}

, , ?

+4
2

, helpfunc , , , . , , .

#include <vector>
#include <iostream>
#include <string>

template <typename T>
void print_2d_vector(std::vector<std::vector<T>>& v)
{
    for(int i = 0; i < v.size(); i++)
    {
        std::cout << "{";
        for(int j = 0; j < v[i].size(); j++)
        {
            std::cout << v[i][j];
            if(j != v[i].size() - 1)
            {
                sizetd::cout << ", ";
            }
        }
        std::cout << "}\n";
    }
}

template <typename T>
struct permcomb2
{
    std::vector<std::vector<T>> end_set;
    std::vector<T>& data;
    permcomb2(std::vector<T>& param) : data(param) {}

    void helpfunc(std::vector<T>& seen, int depth, int current) // Add one more param for the starting depth of our recursive calls
    {
        if(depth == 0)
        {
            end_set.push_back(seen);
        }
        else
        {
            for(int i = current; i < data.size(); i++) // Set the loop to start at given value
            {
                seen.push_back(data[i]);
                helpfunc(seen, depth - 1, i);
                seen.pop_back();
            }
        }
    }
};

template <typename T>
std::vector<std::vector<T>> permtest(std::vector<T>& data, int subset_size)
{
    permcomb2<T> helpstruct(data);
    std::vector<T> empty {};
    helpstruct.helpfunc(empty, subset_size, 0); // Initialize the function at depth 0
    return helpstruct.end_set;
}

using namespace std;
int main()
{
    std::vector<std::string> flavors {"Vanilla", "Chocolate", "Strawberry"};
    auto a1 = permtest(flavors, 2);

    cout << "Return all combinations with repetition\n";
    print_2d_vector(a1);
    return 0;
}
+2

, data.

for (int i = 0; i < data.size(); i++) {
  for (int j = i; j < data.size(); j++) {
    for (int k = j; k < data.size(); k++) {
      // etc...
  }
}

, - subset_size. :

template <class T>
void solution(std::vector<T>& data, std::vector<std::vector<T>>& sol, int subset_size, int start=0, int depth=0) {
  if (depth == subset_size) return;

  // Assume that the last element of sol is a base vector
  // on which to append the data elements after "start"
  std::vector<T> base = sol.back();

  // create (data.size() - start) number of vectors, each of which is the base vector (above)
  // plus each element of the data after the specified starting index
  for (int i = start; i < data.size(); ++i) {
    sol.back().push_back(data[i]);                   // Append i'th data element to base 
    solution(data, sol, subset_size, i, depth + 1);  // Recurse, extending the new base
    if (i < data.size() - 1) sol.push_back(base);    // Append another base for the next iteration
  }
}

template <typename T>
std::vector<std::vector<T>> permtest(std::vector<T>& data, int subset_size) {
  std::vector<std::vector<T>> solution_set;
  solution_set.push_back(std::vector<T>());
  solution(data, solution_set, subset_size);
  return solution_set;
}
+1

Source: https://habr.com/ru/post/1691816/


All Articles