ajout de tous les cours et TP préparés cet été
This commit is contained in:
@@ -6,7 +6,7 @@
|
||||
|
||||
|
||||
|
||||
> En informatique, il est peut-être utile et interessant de determiner la présence ou non d'un motif au sein d'un texte. Par exemple quand vous cherchez un mot précis dans un long document, si vous voulez vous entrainer pour des chiffres et des lettres...
|
||||
> En informatique, il est peut-être utile et intéressant de déterminer la présence ou non d'un motif au sein d'un texte. Par exemple quand vous cherchez un mot précis dans un long document, si vous voulez vous entraîner pour des chiffres et des lettres...
|
||||
|
||||
On se donne un texte et un motif représentés en Python par des chaînes de caractères (*type str*). La question est de déterminer la présence ou l'absence de ce motif.
|
||||
|
||||
@@ -27,16 +27,16 @@ def recherche(texte, motif):
|
||||
for car in texte:
|
||||
if car == motif:
|
||||
return True
|
||||
return False
|
||||
return False
|
||||
```
|
||||
|
||||
Si une des valeur successives de la variable ***car*** est égale à la valeur de la variable ***motif*** alors la fonction renvoie ***True*** et la fonction est interrompue. Si tous les caractères du texte sont examinés et que la bouclle n'est pas interrompue, la fonction renvoie ***False***.
|
||||
Si une des valeurs successives de la variable ***car*** est égale à la valeur de la variable ***motif*** alors la fonction renvoie ***True*** et la fonction est interrompue. Si tous les caractères du texte sont examinés et que la boucle n'est pas interrompue, la fonction renvoie ***False***.
|
||||
|
||||
Nous remplaçons l'instruction ***return True*** par ***return i*** si nous souhaitons obtenir la place du caractère dans le texte. La fonction renvoie alors la place du caractère s'il a été trouvé et rien sinon (donc la valeur ***None***).
|
||||
|
||||
```python
|
||||
def recherche (texte, motif):
|
||||
for i in range (len(texte)):
|
||||
def recherche(texte, motif):
|
||||
for i in range(len(texte)):
|
||||
if texte[i] == motif:
|
||||
return i
|
||||
```
|
||||
@@ -47,7 +47,7 @@ Le coût de cette recherche est linéaire en la longueur de la chaîne, en effet
|
||||
|
||||
### 2. Recherche naïve d'un motif dans un texte
|
||||
|
||||
> On parle de recherche naïve car c'est l'une des premières idées qui peut venir à l'esprit
|
||||
> On parle de recherche naïve car c'est l'une des premières idées qui peut venir à l'esprit.
|
||||
|
||||
- On recherche la présence du premier caractère du motif dans le texte
|
||||
- Si on le trouve, on vérifie si les caractères suivants du motif coïncident avec ceux du texte
|
||||
@@ -61,9 +61,9 @@ Cet algorithme est implémenté dans le programme suivant, où nous pouvons remp
|
||||
def recherche(texte, motif):
|
||||
n = len(texte)
|
||||
m = len(motif)
|
||||
for j in range(n - m +1):
|
||||
for j in range(n - m + 1):
|
||||
i = 0
|
||||
while i < m and texte [j + i] == motif [i]:
|
||||
while i < m and texte[j + i] == motif[i]:
|
||||
i = i + 1
|
||||
if i == m :
|
||||
return j
|
||||
@@ -85,7 +85,7 @@ Si *n* est la longueur du texte et *m* la longueur du motif recherché, alors la
|
||||
En effet la recherche du premier caractère du motif s'arrête lorsqu'il ne reste plus assez de place dans le texte pour placer ce motif.
|
||||
Dans le pire des cas, la boucle interne *while* est parcourue au plus *m* fois, pour tester chaque caractère du motif.
|
||||
|
||||
> Le nombre total de comparaisons entre caractères est donc majoré par *m(n - m + 1)*. Par exemple, si *m = 5* alors on peut dire que le nombre total de comparaisons est majoré par *5n*. Le nombre *m* est compris entre *0 et n* et on peut donc montrer que la valeur maximale du produit *m(n - m + 1)* est *m<sup>2</sup> + m = (n<sup>2</sup> + 2n) / 4* .
|
||||
> Le nombre total de comparaisons entre caractères est donc majoré par *m(n - m + 1)*. Par exemple, si *m = 5* alors on peut dire que le nombre total de comparaisons est majoré par *5n*. Le nombre *m* est compris entre *0* et *n* et on peut donc montrer que la valeur maximale du produit *m(n - m + 1)* est atteinte pour *m ≈ (n + 1) / 2*, donnant une complexité dans le pire cas en **O(n²)**.
|
||||
|
||||
|
||||
|
||||
@@ -95,8 +95,8 @@ def recherche2(texte, motif):
|
||||
n = len(texte)
|
||||
i = 0
|
||||
j = 0
|
||||
while i < m and j < n :
|
||||
if motif [i] != texte[j]:
|
||||
while i < m and j < n:
|
||||
if motif[i] != texte[j]:
|
||||
|
||||
j = j - i + 1
|
||||
i = 0
|
||||
@@ -135,7 +135,7 @@ Après six comparaisons de caractères, nous avons un échec (lettres en gras).
|
||||
| Motif | | **a** | b | c | a | b | c | | | | |
|
||||
| Indice i | | 0 | 1 | 2 | 3 | 4 | 5 | | | | |
|
||||
|
||||
Ici, c'est un échec dès la première compraison, il faut donc à nouveau nous décaler vers la droite.
|
||||
Ici, c'est un échec dès la première comparaison, il faut donc à nouveau nous décaler vers la droite.
|
||||
|
||||
| Indice j | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 |
|
||||
| -------- | ---- | ---- | ----- | ---- | ---- | ----- | ---- | ---- | ---- | ---- | ---- |
|
||||
@@ -153,7 +153,7 @@ Idem ici, on redécale.
|
||||
| Motif | | | | a | b | **c** | | | | | |
|
||||
| Indice i | | | | 0 | 1 | 2 | 3 | 4 | 5 | | |
|
||||
|
||||
Ici, echec à la troisième comparaison. On décale vers la droite.
|
||||
Ici, échec à la troisième comparaison. On décale vers la droite.
|
||||
|
||||
| Indice j | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 |
|
||||
| -------- | ---- | ---- | ---- | ---- | ----- | ----- | ---- | ---- | ---- | ---- | ---- |
|
||||
@@ -181,7 +181,7 @@ Il nous aura donc fallu dix-huit comparaisons pour trouver le motif. Si le derni
|
||||
>
|
||||
> Par exemple, après les six premières comparaisons, on constate que les cinq premiers caractères du texte sont les cinq premiers du motif. Donc, en décalant, on va forcément être amené à comparer le début du motif avec une autre partie du motif lui même...
|
||||
>
|
||||
> Les informaticiens Morris et Pratt ont eu séparément l'idée d'effectuer un pré-traitement du motif qui permet de déterminer, à partir de quelle place la recherche doit se poursuivre, et éviter ainsi de controler les mêmes caractères plusieurs fois.
|
||||
> Les informaticiens Morris et Pratt ont eu séparément l'idée d'effectuer un pré-traitement du motif qui permet de déterminer, à partir de quelle place la recherche doit se poursuivre, et éviter ainsi de contrôler les mêmes caractères plusieurs fois.
|
||||
|
||||
Reprenons le texte "***abcababcabc***" et le motif "***abcabc***"
|
||||
|
||||
@@ -209,7 +209,7 @@ Le motif est donc décalé de trois unités vers la droite.
|
||||
|
||||
Les comparaisons commencent donc au troisième caractère pour lequel nous avons un échec ici.
|
||||
|
||||
Il n'est pas interessant de décaler uniquement d'une unité vers la droite. Le caractère est un "a" et nous savons parfaitement où se trouve ce caractère dans le motif.
|
||||
Il n'est pas intéressant de décaler uniquement d'une unité vers la droite. Le caractère est un "a" et nous savons parfaitement où se trouve ce caractère dans le motif.
|
||||
|
||||
|
||||
|
||||
@@ -224,23 +224,40 @@ Le nombre total de comparaisons est de treize au lieu de dix-huit pour le progra
|
||||
|
||||
|
||||
|
||||
La fonction `traitement` construit un tableau de décalages. Pour chaque position `i` du motif, `s[i]` indique la position où reprendre la comparaison après un échec.
|
||||
|
||||
```python
|
||||
def traitement(motif):
|
||||
"""Construit le tableau des décalages pour Morris-Pratt."""
|
||||
m = len(motif)
|
||||
s = [-1] * m # Tableau des décalages
|
||||
j = -1
|
||||
for i in range(1, m):
|
||||
while j >= 0 and motif[j + 1] != motif[i]:
|
||||
j = s[j]
|
||||
if motif[j + 1] == motif[i]:
|
||||
j += 1
|
||||
s[i] = j
|
||||
return s
|
||||
```
|
||||
|
||||
```python
|
||||
def morris_pratt(texte, motif):
|
||||
m = len (motif)
|
||||
m = len(motif)
|
||||
n = len(texte)
|
||||
i = 0
|
||||
j = 0
|
||||
sol = [] #tableau pour stocker les solutions
|
||||
s = traitement (motif) #modification du programme naïf recherche
|
||||
while i < m and j < n :
|
||||
if i >= 0 and motif[i] != texte[j] :
|
||||
sol = [] # Tableau pour stocker les solutions
|
||||
s = traitement(motif) # Pré-traitement du motif
|
||||
while i < m and j < n:
|
||||
if i >= 0 and motif[i] != texte[j]:
|
||||
i = s[i]
|
||||
else:
|
||||
i += 1
|
||||
j += 1
|
||||
if i >= m : #Si le motif est présent
|
||||
sol.append(j-m) # La position dans le texte est ajoutée à la liste
|
||||
i = 0 # Une nouvelle recherche commence après le motif trouvé
|
||||
if i >= m: # Si le motif est présent
|
||||
sol.append(j - m) # La position dans le texte est ajoutée à la liste
|
||||
i = 0 # Une nouvelle recherche commence après le motif trouvé
|
||||
return sol
|
||||
```
|
||||
|
||||
@@ -262,9 +279,9 @@ Pour la valeur de i, qui permet d'effectuer le bon décalage, on effectue une fo
|
||||
|
||||
Le principe général des algorithmes de recherche textuelle est de comparer un motif à certaines parties du texte, le motif se décalant vers la droite après chaque échec.
|
||||
|
||||
Comme cela est pratiqué avec l'algorithme de Morris et Pratt, le calcul du décalage d'obtient par un pré-traitement du motif. La différence fondamentale avec ce que l'on a vu précédémment, est que ***la comparaison entre le motif et une partie du texte se fait de droite à gauche en commençant par la fin du motif***.
|
||||
Comme cela est pratiqué avec l'algorithme de Morris et Pratt, le calcul du décalage s'obtient par un pré-traitement du motif. La différence fondamentale avec ce que l'on a vu précédemment, est que ***la comparaison entre le motif et une partie du texte se fait de droite à gauche en commençant par la fin du motif***.
|
||||
|
||||
En procédant ainsi, un type de décalage est articulièrement intéresssant : à la premiere différence constatée, on décale le motif vers la droite de manière à faire coïncider le caractère du texte concerné avec un caractère du motif. Si ce n'est pas possible, alors on décale le motif après le caractère.
|
||||
En procédant ainsi, un type de décalage est particulièrement intéressant : à la première différence constatée, on décale le motif vers la droite de manière à faire coïncider le caractère du texte concerné avec un caractère du motif. Si ce n'est pas possible, alors on décale le motif après le caractère.
|
||||
|
||||
|
||||
|
||||
@@ -333,7 +350,56 @@ Il nous faudra ici six comparaisons finales pour affirmer la présence du motif
|
||||
|
||||
Remarque : si la dernière lettre du texte n'était pas un "c", alors nous n'aurions eu besoin que d'une seule comparaison au lieu de six pour affirmer l'absence du motif, soit sept comparaisons au total.
|
||||
|
||||
|
||||
#### Implémentation simplifiée
|
||||
|
||||
Voici une implémentation de l'algorithme de Boyer-Moore utilisant uniquement la règle du mauvais caractère :
|
||||
|
||||
```python
|
||||
def boyer_moore(texte, motif):
|
||||
"""Recherche un motif dans un texte avec Boyer-Moore (règle du mauvais caractère)."""
|
||||
n = len(texte)
|
||||
m = len(motif)
|
||||
|
||||
if m == 0:
|
||||
return 0
|
||||
|
||||
# Pré-traitement : table des dernières occurrences
|
||||
derniere_occurrence = {}
|
||||
for i in range(m):
|
||||
derniere_occurrence[motif[i]] = i
|
||||
|
||||
# Recherche
|
||||
s = 0 # Décalage du motif par rapport au texte
|
||||
while s <= n - m:
|
||||
j = m - 1 # On commence par la fin du motif
|
||||
|
||||
# Comparaison de droite à gauche
|
||||
while j >= 0 and motif[j] == texte[s + j]:
|
||||
j -= 1
|
||||
|
||||
if j < 0:
|
||||
return s # Motif trouvé à la position s
|
||||
else:
|
||||
# Calcul du décalage
|
||||
char_texte = texte[s + j]
|
||||
if char_texte in derniere_occurrence:
|
||||
decalage = j - derniere_occurrence[char_texte]
|
||||
if decalage < 1:
|
||||
decalage = 1
|
||||
else:
|
||||
decalage = j + 1
|
||||
s += decalage
|
||||
|
||||
return -1 # Motif non trouvé
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
Auteur : Florian Mathieu
|
||||
|
||||
Licence CC BY NC
|
||||
|
||||
<a rel="license" href="http://creativecommons.org/licenses/by-nc-sa/4.0/"><img alt="Licence Creative Commons" style="border-width:0" src="https://i.creativecommons.org/l/by-nc-sa/4.0/88x31.png" /></a> <br />Ce cours est mis à disposition selon les termes de la <a rel="license" href="http://creativecommons.org/licenses/by-nc-sa/4.0/">Licence Creative Commons Attribution - Pas d'Utilisation Commerciale - Partage dans les Mêmes Conditions 4.0 International</a>.
|
||||
|
||||
|
||||
|
||||
|
||||
Reference in New Issue
Block a user