Exercice 1 : La recherche naïve, fenêtre après fenêtre
Chercher un MOTIF de longueur dans un TEXTE de longueur , c'est trouver toutes les positions telles que les caractères du texte qui commencent en soient exactement ceux du motif. On appelle FENÊTRE la portion du texte qui va de texte[i] à texte[i + m - 1], celle que l'on compare au motif.
La version naïve ci-dessous sépare les deux rôles : la fonction coincide compare UNE fenêtre au motif, de gauche à droite, et s'arrête au premier caractère différent ; la fonction occurrences fait glisser la fenêtre d'une case à la fois. On appelle COMPARAISON chaque évaluation de texte[i + k] == motif[k].
On étudie l'appel occurrences('balalaika', 'ala').
def coincide(texte, motif, i):
k = 0
while k < len(motif) and texte[i + k] == motif[k]:
k = k + 1
return k == len(motif)
def occurrences(texte, motif):
res = []
for i in range(len(texte) - len(motif) + 1):
if coincide(texte, motif, i):
res.append(i)
return res- a) Donnez , et le nombre de fenêtres examinées par la boucle for. Quelle est la dernière valeur prise par , et pourquoi la boucle ne va-t-elle pas jusqu'à ?
- b) Que renvoie l'appel ? La méthode count de Python, 'balalaika'.count('ala'), renvoie : expliquez l'écart.
- c) Pour chaque fenêtre, donnez le nombre de comparaisons effectuées par coincide, puis le total.
- d) Un élève inverse les deux tests de la boucle while et écrit texte[i + k] == motif[k] and k < len(motif). Que se passe-t-il ?
- e) On cherche le motif 'aaaa' dans un texte formé de lettres a. Combien de fenêtres, d'occurrences et de comparaisons ? Déduisez-en une majoration du nombre de comparaisons de la recherche naïve en fonction de et .
Voir la correction
Réponses
- a) , , fenêtres, va de à
- b) [1, 3] : deux occurrences qui se chevauchent ; count ne compte pas les chevauchements
- c) comparaisons
- d) IndexError dès la première fenêtre qui coïncide entièrement
- e) fenêtres, occurrences, comparaisons ; au plus
a) et . La variable parcourt range(n - m + 1), soit les valeurs à : fenêtres. La dernière fenêtre commence en et se termine en , dernier indice du texte. Une fenêtre qui commencerait en réclamerait texte[9], qui n'existe pas. La borne n'est donc pas un détail : c'est elle qui garantit que chaque fenêtre tient entière dans le texte. Écrire range(len(texte)) est l'erreur la plus fréquente sur ce code, et elle se paie par une IndexError sur les dernières positions.
b) Les fenêtres qui coïncident commencent en (a, l, a aux indices , , ) et en (a, l, a aux indices , , ) : la fonction renvoie [1, 3]. Les deux occurrences se CHEVAUCHENT, elles partagent le a d'indice . La méthode count compte les occurrences sans chevauchement : après avoir trouvé ala en , elle reprend la lecture en et ne voit plus rien. Notre fonction examine chaque fenêtre indépendamment des autres, donc elle trouve les deux. Les deux réponses ne répondent pas à la même question ; en génomique, c'est presque toujours la version avec chevauchement que l'on veut, et c'est celle que demandent les sujets.
c) On compte jusqu'au premier échec INCLUS : , b contre a, comparaison ; , trois égalités, ; , l contre a, ; , trois égalités, ; , l contre a, ; , a égal à a puis i contre l, ; , i contre a, . Total : comparaisons. Une fenêtre qui coïncide coûte comparaisons, une fenêtre qui échoue coûte le rang de l'échec. Le piège de comptage est d'oublier la comparaison qui échoue : elle a bien été faite, c'est même elle qui arrête la boucle.
d) L'opérateur and évalue de gauche à droite et s'arrête dès que le premier test est faux : c'est l'évaluation paresseuse. Dans l'ordre correct, quand atteint len(motif), le test k < len(motif) est faux et l'accès motif[k] n'est jamais tenté. Dans l'ordre inversé, après trois égalités sur la fenêtre , vaut et Python évalue d'abord texte[4] == motif[3] : motif[3] n'existe pas, et le programme s'arrête sur une IndexError. L'erreur ne se produit qu'à la première fenêtre qui coïncide entièrement : un test sur un texte qui ne contient pas le motif ne la détecte pas. Le garde-fou se place TOUJOURS avant l'accès qu'il protège.
e) et : fenêtres, et toutes coïncident, donc occurrences, chacune au prix de comparaisons : . C'est le pire cas possible : aucune fenêtre ne coûte plus de comparaisons et il y a fenêtres, donc la recherche naïve fait au plus comparaisons, de l'ordre de quand le motif est court devant le texte. Pour un motif de caractères dans un texte d'un million, cela peut dépasser vingt millions de comparaisons. Tout l'enjeu de Boyer-Moore est de ne plus examiner chaque fenêtre.
Coche ici les exercices faits ou à revoir : un compte gratuit, sans mot de passe, retient tes coches d'une visite à l'autre et te dit quel chapitre attaquer ensuite. Crée ton espace, un courriel suffit.