Search code examples
c++for-loopmatrixcombinationscartesian-product

Creating a function showing all combinations


I'm having trouble here turning a vector of vectors with different lengths into a matrix with all possible combinations. For example, I am wanting the below

vector <vector <double>> matrix1{{10, 20, 30}, {1}, {2, 3}, {1}};

to turn into

{10, 1, 2, 1}
{10, 1, 3, 1}
{20, 1, 2, 1}
{20, 1, 3, 1}
{30, 1, 2, 1}
{30, 1, 3, 1}

I've come up with the below, which gives me some of the above, but I am obviously missing the ones where c = 3 and a = 20, c = 3 and a = 30.

vector <vector <double>> TransformMatrix(vector<vector<double>> matrix)
{
    vector <vector <double>> fullmatrix;
    for (int a = 0; a < matrix[0].size(); a++)
    {
        vector <double> v1;
        v1.push_back(matrix[0][a]);
        v1.push_back(matrix[1][0]);
        v1.push_back(matrix[2][0]);
        v1.push_back(matrix[3][0]);
        fullmatrix.push_back(v1);
    }
    for (int b = 1; b < matrix[1].size(); b++)
    {
        vector <double> v1;
        v1.push_back(matrix[0][0]);
        v1.push_back(matrix[1][b]);
        v1.push_back(matrix[2][0]);
        v1.push_back(matrix[3][0]);
        fullmatrix.push_back(v1);
    
    }
....
}

Does anyone have any helpful hints / tips here?


Solution

  • What you're trying to do here is obtain the inputs to an n-dimensional Cartesian product. The conventional solution is to write an iterative and recursive function:

    void cartesian_n_pick(const std::vector<std::vector<double>> &in,
                          std::vector<std::vector<double>> &out,
                          std::vector<double> &stack,
                          std::size_t i = 0)
    {
        // base case: there is nothing more to loop over, so we do something
        //            with the current stack
        if (i >= in.size()) {
            out.push_back(stack);
            return;
        }
        // regular case: we loop over in[i] and recurse, which translates into an
        //               n-dimensional for-loop
        for (double x : in[i]) {
            stack.push_back(x);
            cartesian_n_pick(in, out, stack, i + 1);
            stack.pop_back();
        }
    }
    
    int main() {
        std::vector<std::vector<double>> matrix = /* ... */;
        std::vector<std::vector<double>> combinations;
        std::vector<double> stack;
        cartesian_n_pick(in, combinations, stack);
    }
    

    Also note that it's excessive and probably unnecessary to store all results in out. You might be able to just process the stack whenever needed instead of storing all possible stacks in a vector first.

    See also How can I create cartesian product of vector of vectors? for similar solutions.