# TD6: Listes, Dictionnaires
## Préliminaires
#### Objectif
Plusieurs structures de données existent au sein de Python et ont chacune **leurs avantages** (ordre maintenu, garanti d'unicité, ...) et les **performances d'accès** à un élément liées.
Nous allons d'abord utiliser la fonction de Fibonacci pour comparer une implémentation **récursive classique**, **récursive terminale** et **récursive avec un dictionnaire**.
#### Fonctions utiles
- **Dictionnaire :**
```python
# Création
dico = {'cle0': 0}
# Ajout d'éléments
dico['cle1'] = 14 # valeur
dico['cle2'] = 15
# Accès à un élément
dico['cle1']
```
- **Graphes avec Python :**
```python
import matplotlib.pyplot as plt
plt.figure()
plt.semilogy(x, y) # ou plt.plot(x, y)
plt.xlabel('Label de X')
plt.ylabel('Label de Y')
plt.title('Titre')
plt.show()
```
- **Calcul du temps :**
```python
import time
beginning = time.time()
# ou time.time_ns()
# ou time.perf_counter_ns()
# Exécution de la fonction à mesurer
end = time.time() - beginning
```
## Première Partie : Fibonacci
Mesure de la performance de **trois** fonctions de Fibonacci différentes :
1. **Récursive classique :**
```python
def fibo_rec(n):
"""
Fonction Fibonacci récursive. Complexité exponentielle.
"""
if n == 0:
return 1
elif n == 1:
return 1
else:
return fibo_rec(n-1) + fibo_rec(n-2)
```
---
2. **Récursive terminale :**
```python
def fib_rec_term(n, a=1, b=1):
"""
Fonction Fibonacci récursive terminale
"""
if n == 0 :
return a
elif n == 1 :
return b
else :
return fib_rec_term(n - 1, b, a + b)
```
---
3. **Récursive avec dictionnaire :**
**Solution :**
```python
def fib_dict(n, dico={0:0,1:1}):
if n not in dico :
dico[n]= fib_dict(n-1, dico) + fib_dict(n-2, dico)
return dico[n]
```
---
Écrire une fonction `test_fib(fct, nb=30)` qui prend comme argument la fonction à tester et le nombre d'itérations à lui appliquer et qui renvoie la liste de numéros de terme et les temps associés.
Par exemple, `test_fib(fct=fib, nb=4)` renvoie `([0, 1, 2, 3, 4],[0.12, 0.13, 0.14, 0.15])`. Vous pouvez ensuite tracer les graphes associés à l'aide des fonctions données en début de TD.
**Solution :**
```python
def test_fib(fct=fib_rec, nb=30):
ind = []
tps = []
for i in range(nb):
ind.append(i)
t0 = time.time_ns()
fct(i)
tps.append(time.time_ns() - t0)
return ind, tps
```
## Deuxième Partie : Performance des structures de donnees
### Préliminaires
#### Objectif
On veut maintenant comparer quatre structures de données différentes (**listes, ensembles, dictionnaires** et **arbres binaires de recherche**). Pour pouvoir les comparer, notre méthode consistera à **comptabiliser le temps passé à trouver tous les mots d'un premier fichier texte présents dans un second**.
Le premier fichier sera le texte court à trouver dans le second, les mots du vocabulaire anglais (`dico_gb.txt`). On stocke le premier dans **une liste** et le second dans la structure de données que l'on souhaite tester.
**Trois fichiers** sont donnés:
- `dico_gb.txt` le dictionnaire de mots anglais
- `simple_grail.txt` le texte réduit à tester
- `grail.txt` le texte complet à tester
#### Fonctions utiles
- **Lecture de fichier :**
```python
with open(filename) as f:
lines = [line.strip() for line in f]
# Équivalent à :
# for line in f:
# lines.append(line.strip())
```
- **Expression régulière de détection de mot :**
```python
import re
test_str = "Bonjour je m'appelle toto"
# Détection des séparateurs de mots
sepwords = re.compile("[^a-zA-Z']+")
sepwords.split(test_str)
>>> ['Bonjour', 'je', "m'appelle", 'toto']
```
- **Différentes structures :**
- **`Set`**
```python
set_dict = set(list_dict)
```
- **`AFLTree`**
```python
avl_dict = FastAVLTree()
for word in list_dict:
avl_dict.insert(word, None)
```
- **`RBTree`**
```python
rb_dict = FastRBTree()
for word in list_dict:
rb_dict.insert(word, None)
```
### Déroulement
1. Écrire une fonction `lecture_dico(nom, n)` qui lit le fichier donné et stocke les n premiers mots dans une liste. *Note: il y a un mot par ligne donc pas besoin d'utiliser la regex !*
<!--
**Solution :**
```python
def lecture_dico(nom, n):
with open(nom) as f:
voc_file = [line.strip() for line in f]
return voc_file[:n]
```
-->
2. Écrire une fonction `lecture_texte(nom)` qui lit le fichier, sépare le texte mot à mot et stocke ces mots dans une liste.
<!--
**Solution :**
```python
def lecture_texte(nom):
sepwords = re.compile("[^a-zA-Z']+")
ref = []
with open(nom) as f:
for line in f:
ref += sepwords.split(line.strip()):
return ref
```
-->
3. Écrire une fonction `decompte(dico, texte)` qui compte le nombre de mot du texte trouvé dans le dictionnaire donné.
<!--
**Solution :**
```python
def decompte(dico, texte):
mots_trouves = 0
for mot in texte:
if mot in dico:
# Décommenter le print pour voir les mots trouvés
# print('Mot ' + str(mot_texte) + ' trouvé !')
mots_trouves += 1
return mots_trouves
```
-->
4. Essayer le décompte avec les structures de `set`, `FastAVLTree` et `FastRBTree`.
<!--
**Solution :**
```python
# Ensemble
set_dict = set(list_dict)
# Arbre binaire
avl_dict = FastAVLTree()
for word in list_dict:
avl_dict.insert(word, None)
# Arbre rouge noir
rb_dict = FastRBTree()
for word in list_dict:
rb_dict.insert(word, None)
```
-->
5. Afficher les résultats pour une taille de vocabulaire de 100, 300, 1000, 3000, 10000, 30000, 100000, et tous les mots (-1). Vous pouvez afficher un graphique avec en abscisse les différentes tailles et les temps associés en ordonnée.
<!--
**Solution :**
```python
taille_voc = [100, 300, 1000, 3000, 10000, 30000, 100000, -1]
mesures_temps = []
for taille in taille_voc:
t0 = time.time_ns() # ou time.clock()
# FONCTION DE DECOMPTE
t1 = time.time_ns() # ou time.clock()
mesures_temps.append(t1-t0)
plt.xlabel('Taille du vocabulaire anglais testé (mots)')
plt.ylabel('Temps mesuré (ns)')
plt.grid()
plt.plot(taille_voc, mesures_temps)
plt.show()
``` -->
6. Écrire une fonction `decompte_cnt(fichier_voc, fichier_txt, n)` qui utilise un compteur de type `collections.Counter` pour déterminer les 10 mots les plus fréquents ainsi que la fréquence de chacun.
<!--
**Solution :**
```python
def decompte_cnt(fichier_voc, fichier_texte, n):
mots_trouves = 0
cnt = Counter()
voc = lecture_dico(fichier_voc, n)
texte = lecture_texte(fichier_texte)
for mot in texte:
if mot in voc:
cnt[mot] += 1
return cnt.most_commons(10)
```
-->
## Troisième Partie : Fonction de Takeuchi
Écrire la fonction de Takeuchi `tak(x, y, z)` utilisant un dictionnaire pour stocker les résultats.
<!--
**Solution :**
```python
def tak(x, y, z, opt=True):
"""Fonction de Tekeuchi.
Si opt est vrai, utilise un dictionnaire pour mémoriser les résultats.
"""
# Dictionnaire en variable globale
# Il est également possible de le passer en paramètre.
global dic_tak
if not opt or (x, y, z) not in dic_tak:
if y < x:
dic_tak[(x, y, z)] = tak(
tak(x-1, y, z, opt),
tak(y-1, z, x, opt),
tak(z-1, x, y, opt), opt
)
else:
dic_tak[(x, y, z)] = y
return dic_tak[(x, y, z)]
if __name__ == '__main__':
nmax = 42
t0 = time.time()
dic_tak = dict()
for i in range(nmax):
for j in range(nmax):
for k in range(nmax):
print(i, j, k, tak(i, j, k, opt=True))
print(time.time() - t0)
```
-->
###### tags: `python`