Permutations avec répétitions en C++

Résolu/Fermé
mslider Messages postés 109 Date d'inscription jeudi 21 octobre 2004 Statut Membre Dernière intervention 7 mars 2010 - 21 oct. 2008 à 20:52
mamiemando Messages postés 33079 Date d'inscription jeudi 12 mai 2005 Statut Modérateur Dernière intervention 23 avril 2024 - 23 oct. 2008 à 20:16
Bonjour,

je voudrais m'inspirer du programme de mamiemando (voir le sujet Comptabiliser des permutations en C++ dans ce même forum) qui est très rapide pour essayer de faire une liste de permutations avec répetitions.
Je m'explique:
étant donnés un nombre n et un alphabet Z constitué seulement de 4 lettres tel que Z= {A,T,G,C},
produire toutes les chaines de caractères de longueur n=10 pris parmis l'alphabet Z.

Un exemple avec Z = {A, C} et n = 3 nous donnerait un total de 9 motifs:
aaa
aac
aca
caa
acc
cac
cca
ccc

Pour augmenter la rapidité du code on pourrait aussi assigner à chaque lettre A,T,G,C une valeur binaire:
00,01,10,11.
Puis ensuite refaire la reconversion en lettres après les boucles de traitement.

On a alors 2^(2n) motifs en binaire et 4^n motifs en lettres après conversion.

Pouvez-vous m'aider SVP ?

6 réponses

mslider Messages postés 109 Date d'inscription jeudi 21 octobre 2004 Statut Membre Dernière intervention 7 mars 2010
22 oct. 2008 à 12:54
j'ai oublié: dans le cas de l'énoncé ci-dessus on devrait donc obtenir avec n=10 et Z = {A,T,G,C}
4^10=1048576 possibilités, allant de AAAAAAAAAA à TTTTTTTTTT. Je pense que pour n>20 la combinatoire devient énorme et le programme quel qu'il soit prendrait énormément de temps de calcul.
-1
mslider Messages postés 109 Date d'inscription jeudi 21 octobre 2004 Statut Membre Dernière intervention 7 mars 2010
22 oct. 2008 à 21:41
[code]

#include <vector>
#include <iostream>
#include <cassert>


void f(
unsigned prof, // la profondeur courante (nombre de chiffre choisis)
const unsigned & prof_max, // la profondeur max (nombre de chiffre à choisir)
std::vector<short> perm, // la permutation courante
std::vector<bool> marked, // les nombres utilisés
unsigned & compteur // le nombre de permutations rencontrées
){
if (prof < prof_max){
for(unsigned i=1;i<=4;++i){
if (marked[i]) continue;
std::vector<short> perm_suivant = perm;
perm_suivant.push_back(i);
std::vector<bool> marked_suivant = marked;
marked_suivant[i] = true;
f(prof+1,prof_max,perm_suivant,marked_suivant,compteur);
}
}else{ // le nombre a la taille voulue
for(unsigned i=0;i<perm.size();++i) std::cout << perm[i];
std::cout << std::endl;
++compteur;
}
}

void perm(const unsigned & prof_max){
assert(prof_max<=4);
std::vector<short> perm0;
std::vector<bool> marked0(4,false);
unsigned compteur = 0; // partagé par tous les appels recursifs
f(0,prof_max,perm0,marked0,compteur);
std::cout << "compteur = " << compteur << std::endl;
}

int main(){
perm(4);
return 0;
}
[code]

je comprend pas pourquoi ce code me renvoit une dernière ligne 4444 et pas les lignes 1111,2222,3333 !
-1
mamiemando Messages postés 33079 Date d'inscription jeudi 12 mai 2005 Statut Modérateur Dernière intervention 23 avril 2024 7 749
23 oct. 2008 à 12:46
Pour les personnes qui tomberaient sur ce fil, je précise que le programme évoqué par mslider est ici :
http://www.commentcamarche.net/forum/affich 3075042 comptabiliser des permutations en c?#1

Tu peux coder en binaire mais ça ne change pas grand chose au problème, à la limite la conversion binaires ou entiers vers lettres peut se faire simplement au niveau de la fonction d'affichage. En soit vu que la structure n'es jamais stockée en mémoire et qu'il n'y a que n caractères avec n très petit, autant travailler avec des entiers et reprendre comme tu l'as fait le programme que j'ai écrit.

Pour répondre à ta question, le vecteur marked vérifie dans le programme initial qu'un nombre n'a pas été déjà utilisé à ce niveau :
if (marked[i]) continue;

Ainsi, cette ligne interdit d'avoir deux fois le même chiffre dans une combinaison (ce qui exclue des combinaisons comme 1223, 1111 etc...). Si toi au contraire tu les veux, il suffit de commenter cette ligne, et du coup tu peux te passer du vecteur marked :
#include <vector>
#include <iostream>

#define TAILLE_ALPHABET 4

char int_to_char(short x){
    if      (x == 0) return 'A';
    else if (x == 1) return 'T';
    else if (x == 2) return 'G';
    else if (x == 3) return 'C';
    return '?';
}

void f(
    unsigned prof,              // la profondeur courante (nombre de chiffre choisis)
    const unsigned & prof_max,  // la profondeur max (nombre de chiffre à choisir)
    std::vector<short> perm,    // la permutation courante
//  std::vector<bool> marked,   // les nombres utilisés
    unsigned & compteur         // le nombre de permutations rencontrées
){
    if (prof < prof_max){
        for(unsigned i=0;i<TAILLE_ALPHABET;++i){
//          if (marked[i]) continue;
            std::vector<short> perm_suivant = perm;
            perm_suivant.push_back(i);
//          std::vector<bool> marked_suivant = marked;
//          marked_suivant[i] = true;
//          f(prof+1,prof_max,perm_suivant,marked_suivant,compteur);
            f(prof+1,prof_max,perm_suivant,compteur);
        }
    }else{ // le nombre a la taille voulue
        for(unsigned i=0;i<perm.size();++i) std::cout << int_to_char(perm[i]);
        std::cout << std::endl;
        ++compteur;
    }
}

void perm(const unsigned & prof_max){
    std::vector<short> perm0;
//  std::vector<bool> marked0(TAILLE_ALPHABET,false);
    unsigned compteur = 0; // partagé par tous les appels recursifs
//  f(0,prof_max,perm0,marked0,compteur);
    f(0,prof_max,perm0,compteur);
    std::cout << "compteur = " << compteur << std::endl;
}

int main(){
    perm(2);
    return 0;
}
ce qui donne :
AA
AT
AG
AC
TA
TT
TG
TC
GA
GT
GG
GC
CA
CT
CG
CC
compteur = 16


Note : sur CCM les balises de codes sont entre <>, tu as également un bouton prévu pour au-dessus de la boîte dans laquelle tu tapes ton texte

Bonne chance
-1
Char Snipeur Messages postés 9696 Date d'inscription vendredi 23 avril 2004 Statut Contributeur Dernière intervention 3 octobre 2023 1 297
23 oct. 2008 à 14:13
Je vais peut être dire une connerie, mais il ne serai pas plus simple et plus rapide de faire des boucles ?
Bonne chance pour ton programme de bio ;)
-1

Vous n’avez pas trouvé la réponse que vous recherchez ?

Posez votre question
mslider Messages postés 109 Date d'inscription jeudi 21 octobre 2004 Statut Membre Dernière intervention 7 mars 2010
23 oct. 2008 à 15:05
j'avais pensé à faire des boucles au départ avec ce programme qui d'ailleurs n'est pas complet:


void generateNeighbours(int motifwidth, string m, int nn)
{
   int i, j, k, l; string strmotif;
   vector < int > motif;
   vector < int > neigh;
   vector < vector <int> > partial;
  
   for(i = 0; i < m.size(); i++)
   {
       string base = m.substr(i,1);
       motif.push_back(checkBase(base));
   }
  
   original = m;
   nemotifs.push_back(m);
   neighbours.push_back(motif);
  
   neigh = motif;
   if(nn == 1 || nn == 2)
   {
       for(k = motifwidth-1; k >= 0; k--)
       {
           for(j = 0; j <= 3; j++)
           {
               neigh[k] = j;
               if(neigh != motif)
               {
                   neighbours.push_back(neigh);
                   partial.push_back(neigh);
                   for(int g = 0; g < neigh.size(); g++)
                   {
                       strmotif += nucleotideUnhash(neigh[g]);
                   }
                   nemotifs.push_back(strmotif);
                   strmotif.clear();
               }
           }
           neigh = motif;
       }
   }
  
   if(nn == 2)
   {   
       for(i = 0; i < partial.size(); i++)
       {
           neigh = partial[i];
           for(k = motifwidth-1; k >= 0; k--)
           {
               for(j = 0; j <= 3; j++)
               {
                   neigh[k] = j;
                   if(neigh != partial[i])
                   {
                       neighbours.push_back(neigh);
                       for(int g = 0; g < neigh.size(); g++)
                       {
                           strmotif += nucleotideUnhash(neigh[g]);
                       }
                       nemotifs.push_back(strmotif);
                       strmotif.clear();
                   }
               }
           neigh = partial[i];
           }
       }
   }
   checkMotifs();
  
}


mais après avoir vu le programme de mamiemando plus astucieux je me suis arrêté dans mon élan.
en plus il est rapide j'ai fais un test pour une profondeur de 10 soit 4^10 combinaisons, le temps de calcul = 23 secondes !
Qui dit mieux ?

Merci à toi mamiemando pour ton aide précieuse.
-1
mamiemando Messages postés 33079 Date d'inscription jeudi 12 mai 2005 Statut Modérateur Dernière intervention 23 avril 2024 7 749
23 oct. 2008 à 20:16
De rien :-)

Pour les boucles c'est possible mais pas de manière simple. Là c'est un parcours d'arbre (algo exponentiel) donc c'est plus naturel de l'écrire en récursif. Les boucles sont plus indiquées pour des algorithmes polynomiaux (parcours d'un tableau ou d'une matrice par exemple).

Bonne continuation
-1