Synthèse Recherche Textuelle
Fondamental : Algorithme naïf
L’algorithme naïf consiste simplement à comparer un à un, de gauche à droite, les caractères du texte apparaissant dans la fenêtre avec ceux du motif. En cas de non-correspondance, on avance simplement la fenêtre d’un indice vers la droite (si c'est possible).
Impossible d'accéder à la ressource audio ou vidéo à l'adresse :
La ressource n'est plus disponible ou vous n'êtes pas autorisé à y accéder. Veuillez vérifier votre accès puis recharger le média.
Cet algorithme est à connaître par ❤️
def recherche_naive(texte, motif):
''' renvoie la liste des indices (éventuellement vide) des occurrences de de la chaîne motif dans la chaîne texte. '''indices = []
i = 0
while i <= len(texte) - len(motif):
j = 0
while j < len(motif) and texte[i+j] == motif[j]: #attention, j < len() doit être en premier, sinon risque d'erreur Index Out Of Range
j += 1
if j == len(motif): #On a parcouru tout le motif
indices.append(i)
i += 1
return indices
Cet algorithme n'est pas efficace ; concrètement, le temps de recherche est semblable quelque soit le motif recherché.
Fondamental : Algorithme de Boyer-Moore-Horspool
Voici les deux principes de l'algorithme proposé par Nigel Horspool pour optimiser l'algorithme naïf :
Dans la fenêtre glissante, les caractères sont comparés de droite à gauche plutôt que de gauche à droite.
En cas de non-correspondance, le décalage de la fenêtre va dépendre du caractère de texte. Ce principe s’appelle la règle du mauvais caractère.
La règle du mauvais caractère est la suivante :
On regarde le caractère X du texte sur lequel on s'est arrêté (car X n'était pas égal au caractère de rang équivalent dans le motif):
si
Xn'est pas dans le motif, il est inutile de se déplacer "de 1" : on retomberait tout de suite surX, c'est du temps perdu. On se décale donc juste assez pour dépasserX, donc de la longueur du motif cherché.si
Xest dans le motif (sauf à la dernière place du motif !), on va regarder la place de la dernière occurrence deXdans le motif et de déplacer de ce nombre, afin de faire coïncider leXdu motif et leXdu texte.
Impossible d'accéder à la ressource audio ou vidéo à l'adresse :
La ressource n'est plus disponible ou vous n'êtes pas autorisé à y accéder. Veuillez vérifier votre accès puis recharger le média.
Cet algorithme est à comprendre !
def bad_carac_horspool(motif):
""" motif - str, chaîne de caractères Sortie: dict - dictionnaire tel que : - les clefs sont les caractères de motif - les valeurs associées sont leur indice "le plus à droite" dans motif Le dernier caractère de motif n'est pas parcouru """dico = {}
for i in range(len(motif)-1):
dico[motif[i]] = i
return dico
def decalage_horspool(motif, texte, i, j, bad_carac):
""" motif - str, chaîne de caractères texte - str, chaîne de caractères i - int, position de la fenêtre telle que 0 <= i < len(texte) j - int, entier tel que 0 <= j < len(motif) bad_carac - dict, dictionnaire des mauvais caractères (Horspool) Sortie: int - décalage à appliquer à la fenêtre en cas de non correspondance entre texte[i+j] et motif[j] """if texte[i+j] in bad_carac.keys():
k = bad_carac[texte[i+j]]
decalage = max(1, j-k)
else:decalage = j+1
return decalage
def positions_horspool(motif, texte):
""" motif - str, chaîne de caractères texte - str, chaîne de caractères Sortie: list - Tableau des positions où trouver motif dans texte """p = len(motif)
n = len(texte)
bad_carac = bad_carac_horspool(motif)
i = 0
indices = []
while i <= n-p:
j = p-1
while j >= 0:
if motif[j] != texte[i+j]:
decalage = decalage_horspool(motif, texte, i, j, bad_carac)
j = -2
else:j = j-1
if j == -1: # On a parcouru tout le motif : il est présent dans texte
indices.append(i)
decalage = 1
i += decalage
return indices
Un autre code possible pour cet algorithme :
def bad_carac_horspool(motif):
dico = {}
for i in range(len(motif)-1):
dico[motif[i]] = i
return dico
def recherche_BMH(motif, texte):
bad_carac = bad_carac_horspool(motif)
indices = []
i = len(motif) -1
while i < len(texte):
j = 0
while j < len(motif) and motif[len(motif)-1-j] == texte[i-j]: #On remonte le motif à l'envers, tant qu'il y a correspondance et qu'on n'est pas arrivés au début du motif
j += 1
if j == len(motif): #Si on est arrivés au début du motif, c'est qu'on a trouvé le mot.
indices.append(i-len(motif)+1)
i += 1 #On a trouvé le motif, mais attention, il ne faut pas trop se décaler sinon on pourrait rater d'autres occurences du motif (pensez à la recherche du motif «mama» dans le mot «mamamamama»). On se décale donc de 1.
else:if texte[i-j] in bad_carac:
i = max(i - j + len(motif) - bad_carac[texte[i-j]] - 1, i+1) #On décale juste de ce qu'il faut pour mettre en correspondance les lettres, en évitant le retour en arrière (d'où le max pour se décaler au moins de 1)
else:i = i - j + len(motif) #La lettre n'est pas dans le motif : on se positionne juste après elle.
return indices
En appliquant cet algorithme sur des textes et des motifs de longueurs différentes, on constate une chose remarquable : plus le motif recherché est long et plus la recherche est rapide.
Complément : Nombre de comparaisons
Pour calculer le nombre de comparaisons, on ajoute un simple compteur que l'on incrémente à juste avant de rentrer dans le test. Ainsi, pour l'algorithme naïf, on obtient ce code :
def nb_comparaisons_naif(motif, texte):
""" motif - str, chaîne de caractères texte - str, chaîne de caractères Sortie: int - Nombre de comparaisons nécessaires pour déterminer les positions où trouver motif dans texte selon l'algorithme naïf de recherche textuelle >>> motif = 'CAAGT' >>> texte = 'ATCAAGTTCAAGTCAGTCCCCAAGTTGATGCAAGT' >>> nb_comparaisons_naif(motif, texte) 52 """p = len(motif)
n = len(texte)
i = 0
nb_comparaisons = 0
while i <= n-p:
j = 0
while j < p :
nb_comparaisons += 1
if motif[j] == texte[i+j]:
j += 1
else:j = p
i += 1
return nb_comparaisons
Alors que pour l'algorithme optimisé, on peut utiliser celui-ci :
def nb_comparaisons_horspool(motif, texte):
""" motif - str, chaîne de caractères texte - str, chaîne de caractères Sortie: int - Nombre de comparaisons nécessaires pour déterminer les positions où trouver motif dans texte selon l'algorithme de recherche textuelle simplifié de Horspool >>> motif = 'CAAGT' >>> texte = 'ATCAAGTTCAAGTCAGTCCCCAAGTTGATGCAAGT' >>> nb_comparaisons_horspool(motif, texte) 32 """p = len(motif)
n = len(texte)
bad_carac = bad_carac_horspool(motif)
i = 0
nb_comparaisons = 0
while i <= n-p:
j = p-1
while j >= 0:
nb_comparaisons += 1
if motif[j] != texte[i+j]:
decalage = decalage_horspool(motif, texte, i, j, bad_carac)
j = -2
else:j = j-1
if j == -1:
decalage = 1
i += decalage
return nb_comparaisons