Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Howto create combinations of several vectors without hardcoding loops in C++?

I have several data that looks like this:

Vector1_elements = T,C,A
Vector2_elements = C,G,A
Vector3_elements = C,G,T
..... up to ...
VectorK_elements = ...

#Note also that the member of each vector is always 3.

What I want to do is to create all combination of elements in Vector1 through out VectorK. Hence in the end we hope to get this output (using Vector1,2,3):

TCC
TCG
TCT
TGC
TGG
TGT
TAC
TAG
TAT
CCC
CCG
CCT
CGC
CGG
CGT
CAC
CAG
CAT
ACC
ACG
ACT
AGC
AGG
AGT
AAC
AAG
AAT

The problem I am having now is that the following code of mine does that by hardcoding the loops. Since number of Vectors can be varied, we need a flexible way to get the same result. Is there any?

This code of mine can only handle up to 3 Vectors (hardcoded):

#include <iostream>
#include <vector>
#include <fstream>
#include <sstream>
using namespace std;


int main  ( int arg_count, char *arg_vec[] ) {

    vector <string> Vec1;
          Vec1.push_back("T");
          Vec1.push_back("C");
          Vec1.push_back("A");

    vector <string> Vec2;
          Vec2.push_back("C");
          Vec2.push_back("G");
          Vec2.push_back("A");

    vector <string> Vec3;
          Vec3.push_back("C");
          Vec3.push_back("G");
          Vec3.push_back("T");



     for (int i=0; i<Vec1.size(); i++) {
        for (int j=0; j<Vec2.size(); j++) {
            for (int k=0; k<Vec1.size(); k++) {
                cout << Vec1[i] << Vec2[i] << Vec3[k] << endl;
            }
        }
     }



    return 0;
}
like image 757
neversaint Avatar asked Nov 09 '09 09:11

neversaint


3 Answers

You can implement this like an odometer, which leads to the following (works for different-sized vectors):

Say you have K vectors in an array v: v[0], v[1], ... v[K-1]

Keep an array of iterators it (size K) into your vectors, starting with it[i] = v[i].begin(). Keep incrementing it[K-1] in a loop. When any iterator hits the end() of the corresponding vector, you wrap it around to begin() and increment the previous iterator also (so when it[K-1] wraps around, you increment it[K-2]). These increments may "cascade" so you should do them in a loop backwards. When it[0] wraps around, you're done (so your loop condition could be something like while (it[0] != v[0].end())

Putting all that together, the loop that does the work (after setting up the iterators) should be something like:

while (it[0] != v[0].end()) {
  // process the pointed-to elements

  // the following increments the "odometer" by 1
  ++it[K-1];
  for (int i = K-1; (i > 0) && (it[i] == v[i].end()); --i) {
    it[i] = v[i].begin();
    ++it[i-1];
    }
  }

If you're interested in complexity, the number of iterator increments that get performed is easy to calculate. For simplicity here I'll assume each vector is the same length N. The total number of combinations is NK. The last iterator gets incremented each time, so that is NK, and moving back through the iterators this count gets divided by N each time, so we have NK + NK-1 + ... N1; this sum equals N(NK - 1)/(N-1) = O(NK). This also means that the amortized cost per-combination is O(1).

Anyway, in short, treat it like an odometer spinning its digit wheels.

like image 197
Sumudu Fernando Avatar answered Sep 24 '22 10:09

Sumudu Fernando


This will do the trick:

void printAll(const vector<vector<string> > &allVecs, size_t vecIndex, string strSoFar)
{
    if (vecIndex >= allVecs.size())
    {
        cout << strSoFar << endl;
        return;
    }
    for (size_t i=0; i<allVecs[vecIndex].size(); i++)
        printAll(allVecs, vecIndex+1, strSoFar+allVecs[vecIndex][i]);
}

Call with:

printAll(allVecs, 0, "");
like image 24
interjay Avatar answered Sep 23 '22 10:09

interjay


A C++0x solution. Provided, of course, your compiled supports it (currently GCC 4.5 and VS2010, I think).

The following compiles and works with GCC 4.5 using -std=c++0x switch. The use of variadic templates makes it possible to combine arbitrary number of containers. I am sure you can come up with a more idiomatic solution.

#include <vector>       
#include <string>
#include <sstream>
#include <iostream>
#include <algorithm>

typedef std::vector<std::string> myvec;

// Base case.
void combine2(const std::string &row) {
    std::cout << row << std::endl;
}

// Recursive variadic template core function.
template<class T0, class ...T>
void combine2(const std::string &row, const T0& cont0, T...cont_rest) {
    for (auto i = cont0.begin(); i != cont0.end(); ++i) {
        std::stringstream ss;
        ss << row << *i;
        combine2(ss.str(), cont_rest...);
    }
}

// The actual function to call.
template<class ...T>
void combine(T...containers) {
    combine2("", containers...);
}

int main() {
    myvec v1 = {"T", "C", "A"}, v2 = {"C", "G", "A"}, v3 = {"C", "G", "T"};

    combine(v1);
    combine(v1, v2);
    combine(v1, v2, v3);

    // Or even...
    std::vector<std::string> v4 = {"T", "C", "A"};
    std::vector<char> v5 = {'C', 'G', 'A'};
    std::vector<int> v6 = {1 ,2 ,3};

    combine(v4);
    combine(v4, v5);
    combine(v4, v5, v6);

    return 0;
}
like image 26
Alex B Avatar answered Sep 25 '22 10:09

Alex B