Les Dictionnaires

Des tableaux dont les éléments sont accessibles par des clés plutôt que par des indices entiers.

Définir un dictionnaire :

rot13 = {} # initialisation
abc = "abcdefghijklmnopqrstuvwxyz"
for i in range(26) :
	rot13[abc[i]] = abc[(i+13)%26]

Par compréhension :


abc = "abcdefghijklmnopqrstuvwxyz"
rot13 = {abc[i] : abc[(i+13)%26] for i in range(26)}

Utilisation :


def encode(texte) :
    secret = ""
    for char in texte :
        if char in rot13 : # vérifie si char est une clé
            secret += rot13[char]
        else :
            secret += char
    return secret


Comment aurait-on fait seulement avec des listes ?

Parcourir un dictionnaire

Parcourir les clés :

for k in dico.keys() :
	print(k)

équivalent à :

for k in dico :
	print(k)

Pour obtenir la liste des clés :

L_keys = list(dico.keys()) 

Parcourir les valeurs :

for v in dico.values() :
	print(v)

équivalent à :

for k in dico :
	print(dico[k])

Pour obtenir la liste des valeurs :

L_valeurs = list(dico.values()) 

Parcourir les couples clés-valeurs :

for k,v in dico.items() :
	print(f"{k}:{v}")

équivalent à :

for k in dico :
	print(f"{k}:{dico[k]}")

Pour obtenir la liste des tuples (clé,valeur) :

L_couples = list(dico.items()) 

Ajouter/retirer une clé

dico = {}
dico[(46.16,-1.15)] = "La Rochelle"   # ajoute la clé

del dico[(46.16,-1.15)]               # retire la clé

Un tuple est une clé possible,
car c’est une structure non mutable.


Par contre, l’instruction suivante lève une erreur :

dico[[46.16,-1.15]] = "La Rochelle"

TypeError: unhashable type: 'list'

Quelle est la taille du dictionnaire Notes ?


Notes = {"Bob" : 5 , "Joe" : 12 , "Bob" : 10}

2

Les commandements :


Une clé est unique


Une clé est non mutable

En fait, le 2e commandement
est là pour s’assurer du premier…

Principe du hachage

On se fixe au départ une taille de tableau.
Pour Python, c’est 64 bits
(taille d’un espace mémoire).


On utilise ensuite une fonction de hachage
qui transforme l’objet non mutable donné en argument en un entier compris entre $0$ et $2^{64}-1$.

Python donne accès nativement
à la fonction de hachage hash :

>>> hash(28)
28
>>> hash(28.0)
28
>>> hash(28.1)
230584300921372700
>>> hash("2")
7599881246238757835
>>> hash("Eh alors ?")
2149932438138104006
>>> hash("🍇")
3426984333240570475
>>> hash((1,2,3))
529344067295497451

Ses propriétés peuvent être interrogées :


>>> import sys
>>> sys.hash_info
sys.hash_info(width=64, modulus=2305843009213693951, inf=314159, nan=0, imag=1000003, algorithm='siphash24', hash_bits=64, seed_bits=128, cutoff=0)

modulus = $2^{61}-1$ est le plus grand
nombre premier de Mersenne $<2^{64}$
(ça permet d’optimiser la distribution des
entiers obtenus tout en ayant des calculs rapides)

Une fonction de hachage
travaille en temps constant
(complexité en $O(1)$).

Et que faire si le hachage donne le même entier pour deux clés différentes
(ce qu’on appelle une collision) ?


Une idée possible est de commencer
une liste chaînée depuis l’emplacement
déjà occupée du tableau.

Si ça n’arrive pas trop souvent,
l’accès à une valeur et l’insertion d’une nouvelle reste globalement en $O(1)$.

Atout d’un dictionnaire

Sa rapidité !

Supposons que l’on veuille la note de Bob :


# Liste de tuples :
L = [("Joe",12),("Bill",8),("Al",4),("Bob",13),("Tom",9)]
# Dictionnaire
D = { "Joe":12 , "Bill":8 , "Al":4 , "Bob":13 , "Tom":9 }

Pour afficher la note de Bob avec L :

for prenom,note in L :
	if prenom == "Bob" :
		print(note)

ou

for i in range(len(L)) :
	if L[i][0] == "Bob" :
		print(L[i][1])

Complexité ?

$\color{red}O(len(L))$

Pour afficher la note de Bob avec D :


print(D["Bob"])

Complexité ?

$\color{red}O(1)$

On a mis à profit cette rapidité dans l’implémentation de BFS et DFS pour tester
si un sommet avait déjà été visité.


Utiliser une liste répertoriant les sommets visités plutôt qu’un dictionnaire fait passer la complexité de linéaire à quadratique !

Pas bien 🤮

from collections import deque

def parcours_largeur(G,depart):
    file = deque()
    file.append(depart)
    Vus = []
    Sommets = []
    while file : # tant que la file n'est pas vide
        sommet = file.popleft()  
        if not sommet in Vus :
            file += G[sommet]
            Vus.append(sommet)
            Sommets.append(sommet) 
    return Sommets

Bien 😇

from collections import deque

def parcours_largeur(G,depart):
    file = deque()
    file.append(depart)
    Vus = {s : False for s in G}
    Sommets = []
    while file : # tant que la file n'est pas vide
        sommet = file.popleft()  
        if not Vus[sommet] :
            file += G[sommet]
            Vus[sommet] = True
            Sommets.append(sommet) 
    return Sommets

Retour site