¿Cómo puedo crear un producto cartesiano de vector de vectores?

Tengo un vector de vectores que dice vector<vector > items de diferentes tamaños como los siguientes

 1,2,3 4,5 6,7,8 

Quiero crear combinaciones en términos de producto cartesiano de estos vectores como

 1,4,6 1,4,7 1,4,8 and so on till 3,5,8 

Cómo puedo hacer eso ? He buscado varios enlaces y también los he enumerado al final de esta publicación, pero no puedo interpretar eso porque no estoy tan familiarizado con el idioma. ¿Podría algún cuerpo ayudarme con esto?

 #include  #include  #include  using namespace std; int main() { vector<vector > items; int k = 0; for ( int i = 0; i < 5; i++ ) { items.push_back ( vector() ); for ( int j = 0; j < 5; j++ ) items[i].push_back ( k++ ); } cartesian ( items ); // I want some function here to do this. } 

Este progtwig tiene vectores de igual longitud y puse esto para que sea más fácil entender mi estructura de datos. Será muy útil incluso si alguien utiliza otras respuestas de otros enlaces y se integra con esto para obtener el resultado. Muchas gracias

Un par de enlaces miré un Progtwig Dos desde: progtwig

Primero, te mostraré una versión recursiva.

 // Cartesion product of vector of vectors #include  #include  #include  // Types to hold vector-of-ints (Vi) and vector-of-vector-of-ints (Vvi) typedef std::vector Vi; typedef std::vector Vvi; // Just for the sample -- populate the intput data set Vvi build_input() { Vvi vvi; for(int i = 0; i < 3; i++) { Vi vi; for(int j = 0; j < 3; j++) { vi.push_back(i*10+j); } vvi.push_back(vi); } return vvi; } // just for the sample -- print the data sets std::ostream& operator<<(std::ostream& os, const Vi& vi) { os << "("; std::copy(vi.begin(), vi.end(), std::ostream_iterator(os, ", ")); os < < ")"; return os; } std::ostream& operator<<(std::ostream& os, const Vvi& vvi) { os << "(\n"; for(Vvi::const_iterator it = vvi.begin(); it != vvi.end(); it++) { os << " " << *it << "\n"; } os << ")"; return os; } // recursive algorithm to to produce cart. prod. // At any given moment, "me" points to some Vi in the middle of the // input data set. // for int i in *me: // add i to current result // recurse on next "me" // void cart_product( Vvi& rvvi, // final result Vi& rvi, // current result Vvi::const_iterator me, // current input Vvi::const_iterator end) // final input { if(me == end) { // terminal condition of the recursion. We no longer have // any input vectors to manipulate. Add the current result (rvi) // to the total set of results (rvvvi). rvvi.push_back(rvi); return; } // need an easy name for my vector-of-ints const Vi& mevi = *me; for(Vi::const_iterator it = mevi.begin(); it != mevi.end(); it++) { // final rvi will look like "a, b, c, ME, d, e, f" // At the moment, rvi already has "a, b, c" rvi.push_back(*it); // add ME cart_product(rvvi, rvi, me+1, end); add "d, e, f" rvi.pop_back(); // clean ME off for next round } } // sample only, to drive the cart_product routine. int main() { Vvi input(build_input()); std::cout << input << "\n"; Vvi output; Vi outputTemp; cart_product(output, outputTemp, input.begin(), input.end()); std::cout << output << "\n"; } 

Ahora, te mostraré la versión iterativa recursiva que robé descaradamente de @John:

El rest del progtwig es prácticamente el mismo, solo muestra la función cart_product .

 // Seems like you'd want a vector of iterators // which iterate over your individual vectors. struct Digits { Vi::const_iterator begin; Vi::const_iterator end; Vi::const_iterator me; }; typedef std::vector Vd; void cart_product( Vvi& out, // final result Vvi& in) // final result { Vd vd; // Start all of the iterators at the beginning. for(Vvi::const_iterator it = in.begin(); it != in.end(); ++it) { Digits d = {(*it).begin(), (*it).end(), (*it).begin()}; vd.push_back(d); } while(1) { // Construct your first product vector by pulling // out the element of each vector via the iterator. Vi result; for(Vd::const_iterator it = vd.begin(); it != vd.end(); it++) { result.push_back(*(it->me)); } out.push_back(result); // Increment the rightmost one, and repeat. // When you reach the end, reset that one to the beginning and // increment the next-to-last one. You can get the "next-to-last" // iterator by pulling it out of the neighboring element in your // vector of iterators. for(Vd::iterator it = vd.begin(); ; ) { // okay, I started at the left instead. sue me ++(it->me); if(it->me == it->end) { if(it+1 == vd.end()) { // I'm the last digit, and I'm about to roll return; } else { // cascade it->me = it->begin; ++it; } } else { // normal break; } } } } 

Aquí hay una solución en C ++ 11.

La indexación de las matrices de tamaño variable se puede hacer de forma eloquent con aritmética modular.

El número total de líneas en la salida es el producto de los tamaños de los vectores de entrada. Es decir:

 N = v[0].size() * v[1].size() * v[2].size() 

Por lo tanto, el ciclo principal tiene n como la variable de iteración, de 0 a N-1 . En principio, cada valor de n codifica suficiente información para extraer cada uno de los índices de v para esa iteración. Esto se hace en un subloop usando aritmética modular repetida:

 #include  #include  #include  #include  using namespace std; void cartesian( vector >& v ) { auto product = []( long long a, vector& b ) { return a*b.size(); }; const long long N = accumulate( v.begin(), v.end(), 1LL, product ); vector u(v.size()); for( long long n=0 ; n > v { { 1, 2, 3 }, { 4, 5 }, { 6, 7, 8 } }; cartesian(v); return 0; } 

Salida:

 1 4 6 1 4 7 1 4 8 ... 3 5 8 

Código más corto:

 vector> cart_product (const vector>& v) { vector> s = {{}}; for (const auto& u : v) { vector> r; for (const auto& x : s) { for (const auto y : u) { r.push_back(x); r.back().push_back(y); } } s = move(r); } return s; } 

Aquí está mi solución. También iterativo, pero un poco más corto que el anterior …

 void xp(const vector*>& vecs, vector*> *result) { vector*>* rslts; for (int ii = 0; ii < vecs.size(); ++ii) { const vector& vec = *vecs[ii]; if (ii == 0) { // vecs=[[1,2],...] ==> rslts=[[1],[2]] rslts = new vector*>; for (int jj = 0; jj < vec.size(); ++jj) { vector* v = new vector; v->push_back(vec[jj]); rslts->push_back(v); } } else { // vecs=[[1,2],[3,4],...] ==> rslts=[[1,3],[1,4],[2,3],[2,4]] vector*>* tmp = new vector*>; for (int jj = 0; jj < vec.size(); ++jj) { // vec[jj]=3 (first iter jj=0) for (vector*>::const_iterator it = rslts->begin(); it != rslts->end(); ++it) { vector* v = new vector(**it); // v=[1] v->push_back(vec[jj]); // v=[1,3] tmp->push_back(v); // tmp=[[1,3]] } } for (int kk = 0; kk < rslts->size(); ++kk) { delete (*rslts)[kk]; } delete rslts; rslts = tmp; } } result->insert(result->end(), rslts->begin(), rslts->end()); delete rslts; } 

Lo derivé con un poco de dolor de una versión de Haskell que escribí:

 xp :: [[a]] -> [[a]] xp [] = [] xp [l] = map (:[]) l xp (h:t) = foldr (\x acc -> foldr (\l acc -> (x:l):acc) acc (xp t)) [] h 

Parece que desea un vector de iteradores que iteren sobre su vector individual.

Comience todos los iteradores al principio. Construya su primer vector de producto sacando el elemento de cada vector a través del iterador.

Incrementa la más a la derecha y repite.

Cuando llegue al final, reinicie ese uno al principio e incremente el penúltimo. Puede obtener el iterador “penúltimo” sacándolo del elemento vecino en su vector de iteradores.

Continúa ciclando hasta que el último y el penúltimo iterador estén al final. Luego, reinícielos, incremente el tercer iterador. En general, esto puede ser en cascada.

Es como un odómetro, pero con cada dígito diferente en una base diferente.

Como necesitaba la misma funcionalidad, implementé un iterador que calcula el producto cartesiano sobre la marcha, según sea necesario, y lo itera.

Se puede usar de la siguiente manera.

 #include  #include  #include  #include "cartesian.hpp" int main() { // Works with a vector of vectors std::vector> test{{1,2,3}, {4,5,6}, {8,9}}; CartesianProduct cp(test); for(auto const& val: cp) { std::cout < < val.at(0) << ", " << val.at(1) << ", " << val.at(2) << "\n"; } // Also works with something much less, like a forward_list of forward_lists std::forward_list> foo{{"boo", "far", "zab"}, {"zoo", "moo"}, {"yohoo", "bohoo", "whoot", "noo"}}; CartesianProduct bar(foo); for(auto const& val: bar) { std::cout < < val.at(0) << ", " << val.at(1) << ", " << val.at(2) << "\n"; } } 

El archivo cartesian.hpp se ve así.

 #include  #include  #include  #include  #include  //! Class iterating over the Cartesian product of a forward iterable container of forward iterable containers template class CartesianProductIterator : public boost::iterator_facade, std::vector const, boost::forward_traversal_tag> { public: //! Delete default constructor CartesianProductIterator() = delete; //! Constructor setting the underlying iterator and position /*! * \param[in] structure The underlying structure * \param[in] pos The position the iterator should be initialized to. std::numeric_limits::max()stands for the end, the position after the last element. */ explicit CartesianProductIterator(T const& structure, std::size_t pos); private: //! Give types more descriptive names // \{ typedef T OuterContainer; typedef typename T::value_type Container; typedef typename T::value_type::value_type Content; // \} //! Grant access to boost::iterator_facade friend class boost::iterator_core_access; //! Increment iterator void increment(); //! Check for equality bool equal(CartesianProductIterator const& other) const; //! Dereference iterator std::vector const& dereference() const; //! The part we are iterating over OuterContainer const& structure_; //! The position in the Cartesian product /*! * For each element of structure_, give the position in it. * The empty vector represents the end position. * Note that this vector has a size equal to structure->size(), or is empty. */ std::vector position_; //! The position just indexed by an integer std::size_t absolutePosition_ = 0; //! The begin iterators, saved for convenience and performance std::vector cbegins_; //! The end iterators, saved for convenience and performance std::vector cends_; //! Used for returning references /*! * We initialize with one empty element, so that we only need to add more elements in increment(). */ mutable std::vector> result_{std::vector()}; //! The size of the instance of OuterContainer std::size_t size_ = 0; }; template CartesianProductIterator::CartesianProductIterator(OuterContainer const& structure, std::size_t pos) : structure_(structure) { for(auto & entry: structure_) { cbegins_.push_back(entry.cbegin()); cends_.push_back(entry.cend()); ++size_; } if(pos == std::numeric_limits::max() || size_ == 0) { absolutePosition_ = std::numeric_limits::max(); return; } // Initialize with all cbegin() position position_.reserve(size_); for(std::size_t i = 0; i != size_; ++i) { position_.push_back(cbegins_[i]); if(cbegins_[i] == cends_[i]) { // Empty member, so Cartesian product is empty absolutePosition_ = std::numeric_limits::max(); return; } } // Increment to wanted position for(std::size_t i = 0; i < pos; ++i) { increment(); } } template void CartesianProductIterator::increment() { if(absolutePosition_ == std::numeric_limits::max()) { return; } std::size_t pos = size_ - 1; // Descend as far as necessary while(++(position_[pos]) == cends_[pos] && pos != 0) { --pos; } if(position_[pos] == cends_[pos]) { assert(pos == 0); absolutePosition_ = std::numeric_limits::max(); return; } // Set all to begin behind pos for(++pos; pos != size_; ++pos) { position_[pos] = cbegins_[pos]; } ++absolutePosition_; result_.emplace_back(); } template std::vector const& CartesianProductIterator::dereference() const { if(absolutePosition_ == std::numeric_limits::max()) { throw new std::out_of_range("Out of bound dereference in CartesianProductIterator\n"); } auto & result = result_[absolutePosition_]; if(result.empty()) { result.reserve(size_); for(auto & iterator: position_) { result.push_back(*iterator); } } return result; } template bool CartesianProductIterator::equal(CartesianProductIterator const& other) const { return absolutePosition_ == other.absolutePosition_ && structure_ == other.structure_; } //! Class that turns a forward iterable container of forward iterable containers into a forward iterable container which iterates over the Cartesian product of the forward iterable containers template class CartesianProduct { public: //! Constructor from type T explicit CartesianProduct(T const& t) : t_(t) {} //! Iterator to beginning of Cartesian product CartesianProductIterator begin() const { return CartesianProductIterator(t_, 0); } //! Iterator behind the last element of the Cartesian product CartesianProductIterator end() const { return CartesianProductIterator(t_, std::numeric_limits::max()); } private: T const& t_; }; 

Si alguien tiene comentarios sobre cómo hacerlo más rápido o mejor, los agradecería mucho.

Me obligaron a implementar esto para un proyecto en el que estaba trabajando y se me ocurrió el siguiente código. Puede estar atascado en un encabezado y su uso es muy simple, pero devuelve todas las combinaciones que puede obtener de un vector de vectores. La matriz que devuelve solo contiene enteros. Esta fue una decisión consciente porque solo quería los índices. De esta forma, podría indexar cada vector del vector y luego realizar los cálculos que cualquier persona necesitaría … lo mejor es evitar que CartesianProduct contenga “cosas”, es un concepto matemático basado en contar, no en una estructura de datos. Soy bastante nuevo en c ++, pero esto fue probado en un algoritmo de descifrado muy a fondo. Hay alguna recursión ligera, pero en general esta es una implementación simple de un concepto simple de conteo.

 // Use of the CartesianProduct class is as follows. Give it the number // of rows and the sizes of each of the rows. It will output all of the // permutations of these numbers in their respective rows. // 1. call cp.permutation() // need to check all 0s. // 2. while cp.HasNext() // it knows the exit condition form its inputs. // 3. cp.Increment() // Make the next permutation // 4. cp.permutation() // get the next permutation class CartesianProduct{ public: CartesianProduct(int num_rows, vector sizes_of_rows){ permutation_ = new int[num_rows]; num_rows_ = num_rows; ZeroOutPermutation(); sizes_of_rows_ = sizes_of_rows; num_max_permutations_ = 1; for (int i = 0; i < num_rows; ++i){ num_max_permutations_ *= sizes_of_rows_[i]; } } ~CartesianProduct(){ delete permutation_; } bool HasNext(){ if(num_permutations_processed_ != num_max_permutations_) { return true; } else { return false; } } void Increment(){ int row_to_increment = 0; ++num_permutations_processed_; IncrementAndTest(row_to_increment); } int* permutation(){ return permutation_; } int num_permutations_processed(){ return num_permutations_processed_; } void PrintPermutation(){ cout << "( "; for (int i = 0; i < num_rows_; ++i){ cout << permutation_[i] << ", "; } cout << " )" << endl; } private: int num_permutations_processed_; int *permutation_; int num_rows_; int num_max_permutations_; vector sizes_of_rows_; // Because CartesianProduct is called first initially with it's values // of 0 and because those values are valid and important output // of the CartesianProduct we increment the number of permutations // processed here when we populate the permutation_ array with 0s. void ZeroOutPermutation(){ for (int i = 0; i < num_rows_; ++i){ permutation_[i] = 0; } num_permutations_processed_ = 1; } void IncrementAndTest(int row_to_increment){ permutation_[row_to_increment] += 1; int max_index_of_row = sizes_of_rows_[row_to_increment] - 1; if (permutation_[row_to_increment] > max_index_of_row){ permutation_[row_to_increment] = 0; IncrementAndTest(row_to_increment + 1); } } }; 
 #include  #include  void cartesian (std::vector> const& items) { auto n = items.size(); auto next = [&](std::vector & x) { for ( int i = 0; i < n; ++ i ) if ( ++x[i] == items[i].size() ) x[i] = 0; else return true; return false; }; auto print = [&](std::vector const& x) { for ( int i = 0; i < n; ++ i ) std::cout << items[i][x[i]] << ","; std::cout << "\b \n"; }; std::vector x(n); do print(x); while (next(x)); // Shazam! } int main () { std::vector> items { { 1, 2, 3 }, { 4, 5 }, { 6, 7, 8 } }; cartesian(items); return 0; } 

La idea detrás de esto es la siguiente.

Deje n := items.size() .
Deje m_i := items[i].size() , para todo i en {0,1,...,n-1} .
Deje M := {0,1,...,m_0-1} x {0,1,...,m_1-1} x ... x {0,1,...,m_{n-1}-1} .

Primero resolvemos el problema más simple de iterar a través de M Esto se logra con la next lambda. El algoritmo es simplemente la rutina de “carga” que los escolares utilizan para agregar 1, aunque con un sistema mixto de radix.

Usamos esto para resolver el problema más general al transformar una tupla x en M en una de las tuplas deseadas a través de los items[i][x[i]] fórmula items[i][x[i]] para todo i en {0,1,...,n-1} . Realizamos esta transformación en la print lambda.

Luego realizamos la iteración con do print(x); while (next(x)); do print(x); while (next(x)); .

Ahora algunos comentarios sobre la complejidad, bajo el supuesto de que m_i > 1 para todos los i :

  • Este algoritmo requiere O(n) espacio. Tenga en cuenta que la construcción explícita del producto cartesiano toma O(m_0 m_1 m_2 ... m_{n-1}) >= O(2^n) espacio. Así que esto es exponencialmente mejor en el espacio que cualquier algoritmo que requiera que todas las tuplas se almacenen simultáneamente en la memoria.
  • La next función toma O(1) tiempo amortizado (por un argumento de serie geométrica).
  • La función de print toma O(n) tiempo.
  • Por lo tanto, en conjunto, el algoritmo tiene complejidad de tiempo O(n|M|) y complejidad de espacio O(n) (sin contar el costo de almacenar items ).

Una cosa interesante a tener en cuenta es que si la print se reemplaza con una función que inspecciona en promedio solo O(1) coordenadas por tupla en lugar de todas, la complejidad del tiempo cae a O(|M|) , es decir, se vuelve lineal tiempo con respecto al tamaño del producto cartesiano. En otras palabras, evitar la copia de la tupla cada iteración puede ser significativa en algunas situaciones.

Esta versión no admite iteradores o rangos, pero es una implementación directa simple que utiliza el operador de multiplicación para representar el producto cartesiano y una lambda para realizar la acción.

La interfaz está diseñada con la funcionalidad particular que necesitaba. Necesitaba la flexibilidad para elegir vectores sobre los cuales aplicar el producto cartesiano de una manera que no ocultara el código.

 int main() { vector< vector > v{ { 1, 2, 3 }, { 4, 5 }, { 6, 7, 8 } }; (Cartesian(v[0]) * v[1] * v[2]).ForEach( [](long p_Depth, long *p_LongList) { std::cout < < p_LongList[0] << " " << p_LongList[1] << " " << p_LongList[2] << std::endl; } ); } 

La implementación usa recursividad en la estructura de clases para implementar los bucles integrados para cada vector. El algoritmo funciona directamente en los vectores de entrada, no requiere grandes arreglos temporales. Es simple de entender y depurar.

El uso de std :: function p_Action en lugar de void p_Action (long p_Depth, T * p_ParamList) para el parámetro lambda me permitiría capturar variables locales, si quisiera. En la llamada anterior, yo no.

Pero lo sabías, ¿verdad? "función" es una clase de plantilla que toma el parámetro tipo de una función y la hace invocable.

 #include  #include  #include  #include  using namespace std; template  class Cartesian { private: vector &m_Vector; Cartesian *m_Cartesian; public: Cartesian(vector &p_Vector, Cartesian *p_Cartesian=NULL) : m_Vector(p_Vector), m_Cartesian(p_Cartesian) {}; virtual ~Cartesian() {}; Cartesian *Clone() { return new Cartesian(m_Vector, m_Cartesian ? m_Cartesian->Clone() : NULL); }; Cartesian &operator *=(vector &p_Vector) { if (m_Cartesian) (*m_Cartesian) *= p_Vector; else m_Cartesian = new Cartesian(p_Vector); return *this; }; Cartesian operator *(vector &p_Vector) { return (*Clone()) *= p_Vector; }; long Depth() { return m_Cartesian ? 1 + m_Cartesian->Depth() : 1; }; void ForEach(function p_Action) { Loop(0, new T[Depth()], p_Action); }; private: void Loop(long p_Depth, T *p_ParamList, function p_Action) { for (T &element : m_Vector) { p_ParamList[p_Depth] = element; if (m_Cartesian) m_Cartesian->Loop(p_Depth + 1, p_ParamList, p_Action); else p_Action(Depth(), p_ParamList); } }; };