Como criar combinações de vários vetores sem loops de codificação de disco rígidos em C ++?
-
19-09-2019 - |
Pergunta
Eu tenho vários dados que se parece com isso:
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.
O que eu quero fazer é criar todas as combinações de elementos em Vector1 através de fora VectorK. Assim, no final, esperamos obter esta saída (usando 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
O problema que estou tendo agora é que o seguinte código de mina faz que por codificar os loops. Desde número de vetores podem ser variadas, precisamos de uma maneira flexível para obter o mesmo resultado. Existe alguma?
Este código da mina só pode lidar com até 3 Vetores (codificado):
#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;
}
Solução
Isto irá fazer o truque:
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]);
}
Chamada com:
printAll(allVecs, 0, "");
Outras dicas
Você pode implementar isso como um conta-quilómetros, o que leva à seguinte (obras para vetores de tamanhos diferentes):
Digamos que você tenha vetores K em uma matriz v: v[0], v[1], ... v[K-1]
Mantenha um conjunto de iteradores it
(tamanho K) em seus vetores, começando com it[i] = v[i].begin()
. Mantenha incrementando it[K-1]
em um loop. Quando qualquer iterador atinge o end()
do vector correspondente, você envolvê-la em torno de begin()
e incrementar o iterador anterior também (assim quando it[K-1]
envolve, você it[K-2]
incremento). Estes incrementos podem "cascata" de modo que você deve fazê-las em um loop para trás. Quando it[0]
envolve, você está feito (assim sua condição de loop poderia ser algo como while (it[0] != v[0].end())
Colocando tudo isso junto, o loop que faz o trabalho (depois de configurar os iteradores) deve ser algo como:
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];
}
}
Se você estiver interessado em complexidade, o número de iteradoras incrementos que são executadas é fácil de calcular. Para simplificar aqui eu vou assumir cada vetor é o mesmo N. comprimento O número total de combinações é N K . A última iteração é incrementado a cada vez, de modo que é N K , e de volta através dos iterators esta contagem é dividido por N de cada vez em movimento, por isso temos N K + N K-1 + ... N 1 ; esta soma é igual a N (N K - 1) / (N-1) = O (N K ). Isto também significa que o custo amortizado por combinação é O (1).
De qualquer forma, em suma, tratá-lo como um conta-quilómetros girando suas rodas de dígitos.
A C ++ 0x solução. Desde que, claro, seus apoios compilou (atualmente GCC 4.5 e VS2010, eu acho).
Os seguintes compila e funciona com GCC 4.5, usando interruptor -std=c++0x
. O uso de modelos variádicos torna possível combinar número arbitrário de recipientes. Tenho certeza que você pode vir até com uma solução mais idiomática.
#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;
}
A dificuldade básica com recursão aqui é que você precisa manter o controle de toda a lista de índices (ou construir outra coisa a corda de forma incremental, como outro pergunta pontos fora).
Uma maneira conveniente para lidar com esse problema sem a construção de objetos adicionais dentro dos laços é entregar a sua função recursiva um vetor de índices, do mesmo comprimento que o vetor de vetores:
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);
}
}
}
Combinando três vectores é essencialmente o mesmo que o primeiro combinando dois vectores, e, em seguida, combinando a terceira um com o resultado.
Então, tudo se resume a escrever uma função que pode combinar dois vetores.
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;
}
E então algo como:
std::vector< std::string > result = combine(Vec1, Vec2);
result = combine(result, Vec3);
e assim por diante para cada vector que você precisa combinado.
Note que é mais o "caminho C ++" para uso de entrada e de saída iterators I.S.O. passando vetores ao redor, e muito mais eficiente. Na versão acima do vetor é copiado mais e mais ...
Eu simplesmente vetores usados ??para ficar mais perto de seu código original e, esperançosamente, fazer mais sentido para você.
Uma vez que você parece querer cada saída ser o comprimento dos vectores individuais, e você parece saber que cada vector é de 3 elementos de largura a partir
#Note also that the member of each vector is always 3.
usando recursão para uma solução geral parece um pouco exagero aqui.
Você pode usar algo assim:
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]);
}
}
}
}
Eu também estou interessado em construir algum tipo de fácil de enxaguar e repetir combinatória. Estou familiarizado com o tipo de abordagem orientada hodômetro, se quiser, onde você tem índices de pé. Algo ao longo dessas linhas. O ponto é, para construir facilmente as tuplas através de um conjunto arbitrário de vetores não relacionados.
Este não chega a responder à sua pergunta, eu não acho, mas você poderia construir static / projeto combinações de tempo usando uma produção variádica tais como o seguinte, onde T1-3 são tipos arbitrários:
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) {
}
Assumindo que você tem um vector que é algo como isto:
typedef vector<tuple<T1, T2, T3>> CombosVector;
CombosVector combos;
push_back_tupled_combos(combos
, 1, 2, 3
, 4, 5, 6
, 7, 8, 9, ...);
Como eu disse, esta é uma consideração tempo de design. Ele não constrói tuplas em toda uma série tempo de execução de vetores. Esse é o lado de baixo. O lateral-se, no entanto, é que você ganha compreensão tempo de compilação de seus tuples vectored.
Mais uma vez, não é o que você, ou eu mesmo, estão atrás, mas talvez ele ajuda faísca comentários favoráveis.
solução acima PRINTALL irá falhar quando vetores são de não mesmo tamanho.
Fixed essa questão:
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, "");
}
A maneira mais simples de abordar esta questão é usar recursão. A função terá um laço nele e vai chamar-se, fundindo-se com a saída da chamada recursiva. Claro, a recursividade pode ser convertido para iteração se você está preocupado com o espaço de pilha, mas pelo menos como ponto de partida, a solução recursiva provavelmente será mais fácil para você.
Use a função next_permutation implementado em std da STL