如何在没有C ++的硬编码循环的情况下创建几个向量的组合?

时间:2009-11-09 09:58:48

标签: c++ algorithm recursion vector combinations

我有几个看起来像这样的数据:

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.

我想要做的是通过VectorK在Vector1中创建所有元素组合。 因此最后我们希望得到这个输出(使用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

我现在遇到的问题是我的下面的代码通过硬编码循环来做到这一点。 由于Vector的数量可以变化,我们需要一种灵活的方法来获得相同的结果。 有没有?

我的这段代码最多只能处理3个矢量(硬编码):

#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;
}

10 个答案:

答案 0 :(得分:13)

你可以像里程表那样实现这个,它会导致以下内容(适用于不同大小的矢量):

假设您在阵列v中有K个向量:v[0], v[1], ... v[K-1]

将一组迭代器it(大小K)保存到向量中,从it[i] = v[i].begin()开始。继续在循环中递增it[K-1]。当任何迭代器命中相应向量的end()时,你将它包装到begin()并同时递增前一个迭代器(所以当it[K-1]环绕时,你增加it[K-2]) 。这些增量可以“级联”,因此您应该在循环中循环执行它们。当it[0]缠绕时,您就完成了(因此您的循环条件可能类似于while (it[0] != v[0].end())

将所有这些放在一起,完成工作的循环(在设置迭代器之后)应该是这样的:

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];
    }
  }

如果您对复杂性感兴趣,那么执行的迭代器增量的数量很容易计算。为简单起见,我假设每个向量的长度为N.组合的总数是N K 。最后一个迭代器每次递增,因此它是N K ,并且通过迭代器返回这个计数每次除以N,所以我们有N K + N K-1 + ... N 1 ;该总和等于N(N K -1)/(N-1)= O(N K )。这也意味着每个组合的摊余成本为O(1)。

无论如何,简而言之,就像一个旋转数字轮的里程表一样对待它。

答案 1 :(得分:9)

这样可以解决问题:

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]);
}

致电:

printAll(allVecs, 0, "");

答案 2 :(得分:5)

C ++ 0x解决方案。当然,只要你编译支持它(我认为目前是GCC 4.5和VS2010)。

以下使用-std=c++0x开关编译并使用GCC 4.5。使用可变参数模板可以组合任意数量的容器。我相信你可以提出一个更惯用的解决方案。

#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;
}

答案 3 :(得分:3)

这里递归的基本困难是你需要跟踪整个索引列表(或者以增量方式构造字符串,正如另一个问题所指出的那样)。

处理此问题而不在循环中构造其他对象的一种权宜之计是将递归函数传递给索引向量,其长度与向量向量相同:

void printcombos(const vector<vector<string> >&vec,vector<int>&index,int depth) {
  if(depth==index.length()) {
    for(int i=0; i<depth; ++i) {
      cout<<vec[i][index[i]];
    }
    cout<<endl;
  } else {
    const vector<string> &myvec= vec[depth];
    int mylength= myvec.length();
    for(int i=0; i<mylength; ++i) {
      index[depth]=i;
      printcombos(vec,index,depth+1);
    }
  }
}

答案 4 :(得分:1)

组合三个向量与首先组合两个向量基本相同,然后将第三个向量与结果组合。

所以这一切归结为编写一个可以组合两个向量的函数。

std::vector< std::string > combine(std::vector< std::string > const & inLhs, std::vector< std::string > const & inRhs) {
    std::vector< std::string > result;
    for (int i=0; i < inLhs.size(); ++i) {
        for (int j=0; j < inRhs.size(); ++j) {
            result.push_back(inLhs[i] + inRhs[j]);
        }
    }
    return result;
}

然后像:

std::vector< std::string > result = combine(Vec1, Vec2);
result = combine(result, Vec3);
对于你需要组合的每个矢量,

等等。

请注意,使用输入和输出迭代器i.s.o更多的是“C ++方式”。传递向量,更有效率。在上面的版本中,矢量被反复复制......

我只是使用向量来更接近原始代码,希望对你更有意义。

答案 5 :(得分:1)

因为您似乎希望每个输出都是单个向量的长度,并且您似乎知道每个向量是3个元素的宽度

  

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

使用递归来获得一般解决方案似乎有点过头了。

你可以使用类似的东西:

typedef boost::array<std::string, 3> StrVec;
// basically your hardcoded version corrected (Vec2[j] not [i])
void printCombinations(const StrVec &Vec1,
                       const StrVec &Vec2,
                       const StrVec &Vec3) {
    for (int i=0; i<Vec1.size(); i++) {
        for (int j=0; j<Vec2.size(); j++) {
            for (int k=0; k<Vec3.size(); k++) {
                std::cout << Vec1[i] << Vec2[j] << Vec3[k] << std::endl;
            }
        }
    }
}

void foo() {
    typedef std::vector<StrVec> StrVecLvl2;
    StrVecLvl2 vecs;

    // do whatever with it ...

    // iterate with index instead of iterator only to shorten the code
    for (int i = 0; i < vecs.size(); ++i) {
        for (int j = i+1; j < vecs.size(); ++j) {
            for (int k = j+1; k < vecs.size(); ++k) {
                printCombinations(vecs[i], vecs[j], vecs[k]);
            }
        }
    }
}

答案 6 :(得分:1)

我也有兴趣建立一些易于冲洗和重复组合的方法。我熟悉里程表驱动型方法,如果你愿意的话,你有步行指数。沿着那条线的东西。关键是,要在任意一组不相关的向量上轻松构建元组。

这不能完全回答你的问题,我不认为,但你可以使用如下的可变生产来构建静态/设计时间组合,其中T1-3是任意类型:

template<class V>
void push_back_tupled_combos(V& v) {
  // Variadic no-args no-op
}

template<class V, typename A, typename B, typename C, typename... Args>
void push_back_tupled_combos(V& v, A a, B b, C c, Args... args) {
    v.push_back({ a, b, c });
    push_back_tupled_combos(v, args...);
}

template<class V, typename... Args>
void push_back_tupled_combos(V& v, Args... args) {
}

假设你有一个看起来像这样的矢量:

typedef vector<tuple<T1, T2, T3>> CombosVector;

CombosVector combos;

push_back_tupled_combos(combos
  , 1, 2, 3
  , 4, 5, 6
  , 7, 8, 9, ...);
像我说的那样,这是设计时间的考虑因素。它不会在运行时向量范围内构建元组。这是不好的一面。然而,好的一面是,你可以在编译时理解你的矢量元组。

同样,不是你,甚至是我所追求的,但也许它有助于引发有利的反馈。

答案 7 :(得分:1)

当矢量的大小不相同时,printAll解决方案将崩溃​​。

解决了该问题:

 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++)
    {
        if( i < allVecs[vecIndex].size() )
        {
            printAll(allVecs, vecIndex + 1, strSoFar + " " + allVecs[vecIndex][i]);
        }
    }
}

int main()
{
    vector <string> Vec1;
    Vec1.push_back("A1");
    Vec1.push_back("A2");
    Vec1.push_back("A3");
    Vec1.push_back("A4");

    vector <string> Vec2;
    Vec2.push_back("B1");
    Vec2.push_back("B2");

    vector <string> Vec3;
    Vec3.push_back("C1");

    vector<vector<string> > allVecs;
    allVecs.push_back(Vec3);
    allVecs.push_back(Vec1);
    allVecs.push_back(Vec2);

    printAll(allVecs, 0, "");
}

答案 8 :(得分:0)

解决此问题的最简单方法是使用递归。该函数将在其中有一个循环并将调用自身,将自身与递归调用的输出合并。当然,如果您担心堆栈空间,递归可以转换为迭代,但至少作为起点,递归解决方案可能对您来说最简单。

答案 9 :(得分:-1)

使用在stl

的std中实现的next_permutation函数