Les algorithmes qui permettent de trouver une sous-chaine de caractères dans une chaine de caractères plus grande sont des "grands classiques" de l'algorithmique. On parle aussi de recherche d'un motif (sous-chaine) dans un texte. Voici un exemple :
Soit le texte suivant :
"Les sanglots longs des violons de l'automne blessent mon coeur d'une langueur monotone. Tout suffocant et blême, quand sonne l'heure, je me souviens des jours anciens et je pleure."
Question : le motif "vio" est-il présent dans le texte ci-dessus, si oui, en quelle(s) position(s) ? (la numérotation d'une chaine de caractères commence à zéro et les espaces sont considérés comme des caractères)
Réponse : on trouve le motif "vio" en position 23
Les algorithmes de recherche textuelle sont notamment utilisés en bioinformatique.
Comme son nom l'indique, la bioinformatique est issue de la rencontre de l'informatique et de la biologie : la récolte des données en biologie a connu une très forte augmentation ces 30 dernières années. Pour analyser cette grande quantité de données de manière efficace, les scientifiques ont de plus en plus recourt au traitement automatique de l'information, c'est-à-dire à l'informatique.
Comme vous le savez déjà, l'information génétique présente dans nos cellules est portée par les molécules d'ADN. Les molécules d'ADN sont, entre autres, composées de bases azotées ayant pour noms : Adénine (représenté par un A), Thymine (représenté par un T), Guanine (représenté par un G) et Cytosine (représenté par un C).
L'information génétique est donc très souvent représentée par de très longues chaines de caractères, composées des caractères A, T, G et C. Exemple : CTATTCAGCAGTC...
Il est souvent nécessaire de détecter la présence de certains enchainements de bases azotées (dans la plupart des cas un triplet de bases azotées code pour 1 acide aminé et la combinaison d'acides aminés forme une protéine). Par exemple, on peut se poser la question suivante : trouve-t-on le triplet ACG dans le brin d'ADN suivant (et si oui, en quelle position ?):
CAAGCGCACAAGACGCGGCAGACCTTCGTTATAGGCGATGATTTCGAACCTACTAGTGGGTCTCTTAGGCCGAGCGGTTCCGAGAGATAGTGAAAGATGGCTGGGCTGTGAAGGGAAGGAGTCGTGAAAGCGCGAACACGAGTGTGCGCAAGCGCAGCGCCTTAGTATGCTCCAGTGTAGAAGCTCCGGCGTCCCGTCTAACCGTACGCTGTCCCCGGTACATGGAGCTAATAGGCTTTACTGCCCAATATGACCCCGCGCCGCGACAAAACAATAACAGTTTGCTGTATGTTCCATGGTGGCCAATCCGTCTCTTTTCGACAGCACGGCCAATTCTCCTAGGAAGCCAGCTCAATTTCAACGAAGTCGGCTGTTGAACAGCGAGGTATGGCGTCGGTGGCTCTATTAGTGGTGAGCGAATTGAAATTCGGTGGCCTTACTTGTACCACAGCGATCCCTTCCCACCATTCTTATGCGTCGTCTGTTACCTGGCTTGGCAT
Nous allons commencer par le premier algorithme qui nous vient à l'esprit (on parle souvent d'algorithme "naïf") :
Cet algorithme naïf peut, selon les situations demander un très grand nombre de comparaisons, ce qui peut entraîner un très long temps de "calcul" avec des chaines très très longues. L'algorithme de Boyer-Moore permet de faire mieux en termes de comparaisons à effectuer
def recherche_naive(texte, mot):
'''
renvoie la liste des indices (éventuellement vide) des occurrences de
de la chaîne `mot` dans la chaîne `texte`.
'''
indices_occurence = []
i = 0
while i <= len(texte) - len(mot):
k = 0
while k < len(mot) and texte[i+k] == mot[k]:
k += 1
if k == len(mot):
indices_occurence.append(i)
i += 1
return indices_occurence
À l'aide du module time, mesurer le temps de recherche pour le texte suivant: "Lorem ipsum dolor sit amet consectetur adipisicing elit. Perferendis molestias quasi commodi labore ducimus quidem, illo magni incidunt. Velit cupiditate animi modi dolorem voluptatem eaque perspiciatis repudiandae aperiam earum doloremque!"
ajouter les lignes aux bons endroits😜
import time
t0 = time.time()
print(time.time()-t0)
même chose mais avec un texte plus long, télécharger le fichier suivant:verne
insérer le code suivant:
with open('verne.txt') as f:
roman = f.read().replace('\n', ' ')
mesurer le temps de recherche d'un mot court, d'une longue phrase (présente dans le texte), d'un mot qui n'existe pas. Que remarquez-vous ?
L'algorithme de Boyer-Moore se base sur les caractéristiques suivantes :
Examinons un exemple. Soit la chaine suivante :
CAATGTCTGCACCAAGACGCCGGCAGGTGCAGACCTTCGTTATAGGCGATGATTTCGAACCTACTAGTGGGTCTCTTAGGCCGAGCGGTTCCGAGAGATAGTGAAAGATGGCTGGGCTGTGAAGGGAAGGAGTCGTGAAAGCGCGAACACGAGTGTGCGCAAGCGCAGCGCCTTAGTATGCTCCAGTGTAGAAGCTCCGGCGTCCCGTCTAACCGTACGCTGTCCCCGGTACATGGAGCTAATAGGCTTTACTGCCCAATATGACCCCGCGCCGCGACAAAACAATAACAGTTT
et le motif : CGGCAG
On peut remarquer que l'on a bien, en fonction des cas, effectué plusieurs décalages en un coup, ce qui, au bout du compte, permet de faire moins de comparaison que l'algorithme naïf. On peut aussi remarquer que plus le motif est grand et plus l'algorithme de Boyer-Moore sera efficace.
Appliquez l'algorithme de Boyer-Moore au cas suivant :
chaine :
CAATGTCTGCACCAAGACGCCGGCAGGTGCAGACCTTCGTTATAGGCGATGATTTCGAACCTACTAGTGGGTCTCTTAGGCCGAGCGGTTCCGAGAGATAGTGAAAGATGGCTGGGCTGTGAAGGGAAGGAGTCGTGAAAGCGCGAACACGAGTGTGCGCAAGCGCAGCGCCTTAGTATGCTCCAGTGTAGAAGCTCCGGCGTCCCGTCTAACCGTACGCTGTCCCCGGTACATGGAGCTAATAGGCTTTACTGCCCAATATGACCCCGCGCCGCGACAAAACAATAACAGTTT
motif : ACCTTCG
Étudiez attentivement le programme Python suivant :
NO_CAR = 256
def recherche(txt, motif):
m = len(motif)
n = len(txt)
tab_car = [-1]*NO_CAR
for i in range(m):
tab_car[ord(motif[i])] = i
decalage = 0
res = []
while(decalage <= n-m):
j = m-1
while j>=0 and motif[j] == txt[decalage+j]:
j = j - 1
if j<0:
res.append(decalage)
if decalage+m<n :
decalage = decalage + m-tab_car[ord(txt[decalage+m])]
else :
decalage = decalage + 1
else:
decalage = decalage + max(1, j-tab_car[ord(txt[decalage+j])])
return res
Testez le programme du "À faire vous-même 2" avec l'exemple proposé au "À faire vous-même 1".
Pour implémenter efficacement cet algorithme, on va passer par un pré-traitement du nom pour facilement accéder au décalage à effectuer. On utilise un dictionnaire pour cela.
def pre_traitement(mot):
"""Renvoie un dictionnaire avec pour clé la lettre et pour valeur le décalage
Arguments
---------
mot: str
Returns
-------
dict
"""
n = len(mot)
décalages = {}
# Il n'est pas nécéssaire d'inclure la dernière lettre
for i, letter in enumerate(mot[:-1]):
décalages[letter] = n - i -1
return décalages
def recherche_mot_boyer(texte, mot):
"""Recherche un mot dans un texte avec l'algo de boyer-moore
Arguments
---------
texte: str
le texte dans lequel on effectue la recherche
mot: str
le mot recherché
Returns
-------
bool
renvoie True si le mot est trouvé
"""
N = len(texte)
n = len(mot)
# création de notre dictionnaire de décalages
décalages = pre_traitement(mot)
# on commence à la fin du mot
i = n - 1
while i < N:
lettre = texte[i]
if lettre == mot[-1]:
# On vérifie que le mot est là avec un slice sur texte
# On pourrait faire un while
if texte[i-n+1:i+1] == mot:
return True
# on décale
if lettre in décalages.keys():
i += décalages[lettre]
else:
i += n
return False
Inspiré du site de David Roche