3

I have been searching for weeks on how to come up with piece of code which I could applied the cartesian product. Let's say I have two arrays :

int M[2]= {1,2};
int J[3] = {0,1,2};

So the code will takes those two arrays in apply the rule M X J therefore we will have the pairs (1,0)(1,1)(1,2)(2,0)(2,1)(2,2) and I want the new result to be saved into a new array where each index in the array contains a pair , for example c[0] = (1,0). Help please :(

mello
  • 95
  • 1
  • 1
  • 8
  • A `std::vector>` should do it. – R Sahu Apr 04 '15 at 20:56
  • I am not good with vectors ... do you have any example with array please @RSahu – mello Apr 04 '15 at 20:57
  • Probably, you should look [here](http://stackoverflow.com/questions/5279051/how-can-i-create-cartesian-product-of-vector-of-vectors) and [here](http://stackoverflow.com/questions/2405242/cartesian-product-of-several-vectors) – kvorobiev Apr 04 '15 at 20:57
  • See [`std::vector::push_back`](http://en.cppreference.com/w/cpp/container/vector/push_back). – R Sahu Apr 04 '15 at 21:00
  • I read all those articles but they all use vectors and i am not good when it comes to vector any ideas how to use it on array ... I appreciate the help :( @kvorobiev – mello Apr 04 '15 at 21:02
  • Perhaps a tutorial on `std::vector` will help. http://www.cprogramming.com/tutorial/stl/vector.html – R Sahu Apr 04 '15 at 21:06
  • `M X J` is union over all `x` which belongs to `M `of unions over all `y` which belongs to `J` of singleton sets `{(x, y)}`. To construct one such `M x J`, you should iterate over all `x` from `M` and combine it with each `y` from `J`. In program, you can do that using double `for-cycle` which you can see in answers. –  Apr 04 '15 at 21:14
  • Learn vectors and you will never look back. – Neil Kirk Apr 04 '15 at 22:03

5 Answers5

5
#include <iostream>
#include <iterator>
#include <vector>
#include <utility>
#include <tuple>

template<typename Range1, typename Range2, typename OutputIterator>
void cartesian_product(Range1 const &r1, Range2 const &r2, OutputIterator out) {
    using std::begin; using std::end;
    
    for (auto i = begin(r1);i != end(r1); ++i) {
        for (auto j = begin(r2); j != end(r2); ++j) {
            *out++ = std::make_tuple(*i, *j);
        }
    }
}

int main() {
    std::vector<int> a{1,2,3};
    std::vector<char> b{'a','b','c','d','e','f'};
    
    std::vector<std::tuple<int, char>> c;
    cartesian_product(a, b, back_inserter(c));
    
    for (auto &&v : c) {
        std::cout << "(" << std::get<int>(v) << "," << std::get<char>(v) << ")";
    }
}

Prints:

(1,a)(1,b)(1,c)(1,d)(1,e)(1,f)(2,a)(2,b)(2,c)(2,d)(2,e)(2,f)(3,a)(3,b)(3,c)(3,d)(3,e)(3,f)

And you can also apply the function to your case:

template<typename T, int N> constexpr int size(T (&)[N]) { return N; }

int main() {
    int M[2] = {1,2};
    int J[3] = {0,1,2};

    std::tuple<int, int> product[size(M) * size(J)];

    cartesian_product(M, J, product);

    for (auto &&v : product) {
        std::cout << "(" << std::get<0>(v) << "," << std::get<1>(v) << ")";
    }
}

The output is:

(1,0)(1,1)(1,2)(2,0)(2,1)(2,2)

http://coliru.stacked-crooked.com/a/3ce388e10c61a3a4

bames53
  • 86,085
  • 15
  • 179
  • 244
  • I try to run the code its says the cartesian_product was not declare – mello Apr 04 '15 at 21:31
  • @mello I'm guessing you copied the second example but did not include the rest of the program from the first example. – bames53 Apr 04 '15 at 21:34
  • Your Program works ... I have some questions : the for loop you use to display the result .. can you give me some explanation because i never use template in my life lol .. i google it just now its like you are using some next level stuff .. – mello Apr 04 '15 at 21:44
  • @mello The `for(auto &&v: c)` is a C++11 feature called a range-based for-loop. The expression after the colon must conform to a particular API that supports accessing every element in the range. Arrays and vectors both work. The part before the colon is a declaration of a variable that will be initialized with each element in the range. The first time around the loop `v` is the first element, the second time it's the second element, etc. – bames53 Apr 04 '15 at 23:09
  • @mello The loop body is a basic use of `std::cout`, except that the thing I'm printing is a `tuple`. A tuple (pronounced two-pull) generalizes of the idea of a pair, triplet, quadruplet, etc., to any number. In this case I'm using it with just two items, so it's essentially a pair. The C++ construct `std::tuple` has a general way to get a particular item out of the tuple, which is the `std::get()` function. `get<0>(v)` get's the item at index 0, `get<1>(v)` gets the item at index 1, etc. So the code is just printing each item in the pair. – bames53 Apr 04 '15 at 23:17
  • @mello The reason `get<1>(v)` uses angle brackets instead of syntax like `v[0]`, `v[1]`, etc. is complicated and understanding it requires a pretty solid grasp of C++. I would recommend just taking it for granted for the time being. If you're really interested you can start by looking into C++ templates. – bames53 Apr 04 '15 at 23:23
  • The code gives `no matching function for call to 'get(std::tuple&)'` error on `g++-10.0.2` – Lars Malmsteen Oct 17 '20 at 14:35
  • @LarsMalmsteen Add a missing `#include ` and see if it works then. – bames53 Oct 19 '20 at 23:55
5

Here is an implementation where the sequences of values is a parameter (rather than pre-known as in all the other implementations):

void CartesianRecurse(vector<vector<int>> &accum, vector<int> stack,
    vector<vector<int>> sequences, int index)
{
    vector<int> sequence = sequences[index];
    for (int i : sequence)
    {       
        stack.push_back(i);
        if (index == 0)
            accum.push_back(stack);
        else
            CartesianRecurse(accum, stack, sequences, index - 1);
        stack.pop_back();
    }
}
vector<vector<int>> CartesianProduct(vector<vector<int>> sequences)
{
    vector<vector<int>> accum;
    vector<int> stack;
    if (sequences.size() > 0)
        CartesianRecurse(accum, stack, sequences, sequences.size() - 1);
    return accum;
}

main() {
    vector<vector<int>> sequences = { {1,2,7},{3,4},{5,6} };
    vector<vector<int>> res = CartesianProduct(sequences);
    // now do something with the result in 'res'.
}
1

Here is an simple example of implementing Cartesian product using vector. Vectors are much better choice as we do not need to worry about its size as it dynamically changes it.

#include <iostream>
#include <vector>
#include <utility>
using namespace std;

int main() {
    int M[2]= {1,2};
    int J[3] = {0,1,2};
    vector<pair<int,int>> C;

    for (int i = 0; i < sizeof(M)/sizeof(M[0]); i++)
    {
        for (int j = 0; j < sizeof(J)/sizeof(J[1]); j++)
        {
            C.push_back(make_pair(M[i],J[j]));
        }  
    }

    /*
    for (vector<int>::iterator it = C.begin(); it != C.end(); it++)
    {
        cout << *it << endl;
    }

    */

    for (int i = 0; i < C.size(); i++)
    {
        cout << C[i].first << "," << C[i].second << endl;
    }
}

Here is the link where I implemented the above code. Although I wouldn't post solution directly relating to your question, links posted in the comments already contains answer which is why I posted.

sam
  • 2,033
  • 2
  • 10
  • 13
  • I have a question : why you don't use a regular for loop to print out the c array ? like for (int i =0 ;i – mello Apr 04 '15 at 21:12
  • 1
    A cartesian product doesn't actually multiply the values together. It creates all possible pairs of elements such that each pair has one element from each input set. http://en.wikipedia.org/wiki/Cartesian_product – bames53 Apr 04 '15 at 21:15
  • @mello you can print out vector like that. See edited solution – sam Apr 04 '15 at 21:16
  • @mello Because using of iterators is a regular way of working with stl containers. – kvorobiev Apr 04 '15 at 21:16
  • @bames53 Oops, I guess I should have researched before blindly posting solution by looking at M x J (which I thought is multiplication). I will research and edit my solution. Thanks. – sam Apr 04 '15 at 21:17
  • @sam2090 you're doing the right thing except you want something like `vector> C;` and `C.push_back(make_pair(M[i], J[j]));` – bames53 Apr 04 '15 at 21:18
  • @sam2090 i run your code but it doesn't print out the right value 0 1 2 0 2 4 – mello Apr 04 '15 at 21:22
  • Thanks @sam2090 it works , like i have some questions about your code but y'all did a great job help me I was stuck in this part for 4 weeks ... Thank you again everyone – mello Apr 04 '15 at 21:50
1

I think using of c++ two-dimensional arrays is a very bad idea, but if you want, you probably could use this code

    #include <iostream>    
    int** cartesian_prod( int* s1, int* s2, int s1size, int s2size )
    {
        int ressize = s1size*s2size;
        int** res = new int*[ressize];
        for ( int i = 0; i < s1size; i++ )
            for ( int j = 0; j < s2size; j++ )
            {
                res[i*s2size+j] = new int[2];
                res[i*s2size+j][0] = s1[i];
                res[i*s2size+j][1] = s2[j];
            }
        return res;
    }
    int main() {
        int M[2]= {1,2};
        int J[3] = {0,1,2};
        int** res;
        int Msize = sizeof(M)/sizeof(M[0]);
        int Jsize = sizeof(J)/sizeof(J[1]);
        res = cartesian_prod(M, J, Msize, Jsize);
        for ( int i = 0; i < Msize*Jsize; i++ )
            std::cout << res[i][0] << " " << res[i][1] << std::endl;
        for (int i = 0; i < Msize*Jsize; i++)
            delete[] res[i];
        delete[] res;
        return 0;
    }

But it is much better to deal with std::vector - it much faster (in terms of development time) and will save you from many errors.

kvorobiev
  • 5,012
  • 4
  • 29
  • 35
1

Solution without for loops.

#include<array>
#include<iostream>
#include<tuple>
#include<utility>

template
<typename T, typename Tuple, std::size_t... I>
auto cartesian_product_base(
                         const T& a,
                         const Tuple& t,
                         std::index_sequence<I...>) {
    return std::make_tuple(std::make_pair(a, std::get<I>(t))...);
}

template
<typename T, typename... Ts, std::size_t... I>
std::array<T, sizeof...(Ts) + 1> to_array(std::tuple<T, Ts...> t, std::index_sequence<I...>) {
    return {std::get<I>(t)...};
}

template
<typename Tuple1, typename Tuple2, std::size_t... I>
auto cartesian_product_impl(
                         const Tuple1& t1,
                         const Tuple2& t2,
                         std::index_sequence<I...>) {
    return std::tuple_cat(cartesian_product_base(
                                 std::get<I>(t1),
                                 t2,
                                 std::make_index_sequence<std::tuple_size<Tuple2>::value>{})...);
}

template
<typename T1, std::size_t N1, typename T2, std::size_t N2>
auto cartesian_product(
                     const std::array<T1, N1>& a1,
                     const std::array<T2, N2>& a2) {
    return to_array(
                    cartesian_product_impl(a1, a2, std::make_index_sequence<N1>{}),
                    std::make_index_sequence<N1 * N2>{});
}

using namespace std;

int main() {
    array<int, 2> M = {1, 2};
    array<int, 3> J = {0, 1, 2};
    auto C = cartesian_product(M, J);
    cout << C.size() << endl;
    cout << "{";
    for (size_t i = 0; i != C.size(); ++i) {
        if (i != 0) {
            cout << ", ";
        }
        cout << "(" << C[i].first << ", " << C[i].second << ")";
    }
    cout << "}" << endl;
}