ACTTGTGA
et CTGTGAAA
on pourrait utiliser:
ACTTGTG--A
-CT-GTGAAA
Notations: si est une chaines de caractères, on note la longueur de et le -ème caractère de la chaine .
Hypothèse: on suppose que le score de l'alignement de deux gaps "-" est négatif. Ceci assure qu'on n'insère un nombre fini de "-" pour obtenir le score maximum, le probleme de maximisation est alors une recherche de maximum dans un nombre fini de possibilités et admet donc une solution (pas forcément unique).
Si et deux chaines de longueur et , on note le score maximum d'alignement possible pour et . On va calculer par un procédé de récurrence à deux indices qui ressemble dans son concept au calcul des coefficients du triangle de Pascal.
On va donc calculer tous les pour et . Supposons qu'on souhaite calculer avec et non nuls. On regarde quelle peut être la dernière paire de caractères alignés. Vu l'hypothèse faire sur la fonction score, il y a 3 possibilités:
Il suffit ensuite d'initialiser et pour
et ce qui est relativement simple puisque ou
est l'alignement de lettres avec "-":
Par exemple si on se donne S=ACTG et T=AACC, on obtient (avec la fonction
score valant 2 et -1):
j | 0 | 1 | 2 | 3 | 4 | |
i | A | A | C | C | ||
0 | 0 | -1 | -2 | -3 | -4 | |
1 | A | -1 | 2 | 1 | 0 | -1 |
2 | C | -2 | 1 | 1 | 3 | 2 |
3 | T | -3 | 0 | 0 | 2 | 2 |
4 | G | -4 | -1 | -1 | 1 | 1 |
L'alignement optimal a donc pour score 1.
En C++, on utilisera un tableau d'entiers à deux indices, qu'on peut
créer de la manière suivante:
vector<int> ligne(m+1,0);
vector< vector<int> > V(n+1,ligne);
On initialise ensuite V[0][j]
et V[j][0]
(faire une boucle
à chaque fois).
On exécute alors une double boucle (l'une imbriquée dans
l'autre), par exemple on fait varier i
de 1 à
puis à i
fixé, j
de 1 à et on applique
la formule de récurrence (1)
Amélioration: on remarque que la formule (1) ne
fait intervenir que la ligne précédente ()
et la ligne actuelle (), on peut donc se contenter de stocker
seulement les 2 lignes Vprecedent[m+1]
et Vactuel[m+1]
On peut ensuite chercher à retracer les alignements de score 1.
Pour cela on part de et on retrace dans (1)
quel(s) terme(s) ont donné lieu au max. Cela revient à regarder
dans les cases au-dessus, à gauche, ou en diagonale à gauche au-dessus
quel(s) est (sont) la (les) case(s) telles que
la différence entre les 2 scores correspond bien
au score de l'alignement des 2 lettres (ou gap) qu'on rajoute. Par exemple
ici, on part de , on peut remonter en ou
puis on se retouve en dans les deux cas. Ensuite on ne
peut passer qu'en même si a la même valeur que
car le score de est plus grand que les précédents
il y a donc eu alignement de 2 lettres. Depuis on a à
nouveau 2 possibilités et et on termine en
ce qui donne 4 possibilités d'alignement de score 1:
A-CTG -ACTG A-CTG -ACTG
AACC- AACC- AAC-C AAC-C
Pour programmer la recherche des chemins, on note dans un tableau au fur
et à mesure du calcul de V[i][j]
les directions qui ont donné lieu au max
de la formule.
On peut pour cela utiliser un type structuré, par exemple:
struct element { int valeur; bool en_haut; bool a_gauche; bool diag; }; vector<element> ligne(m+1); vector< vector<element> > V(n+1,ligne);Si le score
V[i][j]
est obtenu en utilisant la diagonale, on
écrira par exemple:
V[i][j].diag=true;
calcule_chemin
dont le rôle
est d'ajouter à une liste d'alignements les alignements dont on connait
la fin, plus précisément qui se
terminent à la position (pos_a
,pos_b
)
par les chaines de caractères fin_a
et fin_b
:
void calcule_chemin(const string & a,const string & b, const vector< vector<element> > & v, string fin_a, string fin_b, int pos_a, int pos_b, vector<string> & alignements){ ... }Dans
calcule_chemin
, on teste d'abord si pos_a
ou pos_b
est nul. Par exemple si pos_a
est nul,
l'alignement est obtenu en rajoutant pos_b
carcatères -
à fin_a
et en rajoutant le début (les pos_b
premiers
caractères) de b
à fin_b
.
Si pos_a
et pos_b
sont non nuls, on teste les trois
possibilités (par en haut, par la gauche, par la diagonale)
et on appelle récursivement la fonction d'affichage d'alignement
par exemple pour un déplacement vertical:
if (v[pos_a][pos_b].en_haut) calcule_chemin(a,b,v,a[pos_a-1]+fin_a,'-'+fin_b,pos_a-1,pos_b,alignements);