Plan Langage Java
• Valeurs et références
• Accès aux composants d'une classe
• Variables statiques
• Méthodes statiques Algorithmique
• Borne inférieure sur les tris
• Tables : recherche, insertion, suppression
• Hachage
Types par référence
En Java, les objets de type primitif sont transmis par valeur.
Autrement dit, quand la valeur d'un paramètre est un entier, un booléen, un réel ou un caractère, cette valeur est simplement copiée.
Les objets de tous les autres types (les classes) sont transmis par référence (ou adresse) et sont initialisés par new.
Point z = new Point();
Accès aux composants
• A l'intérieur d'une classe, on accède aux variables et aux méthodes de la classe directement par leur nom.
• On peut accéder aux composants d'un objet d'une autre classe en utilisant "."
Variables statiques class Point
{ int x;
int y;
public static Point origine
= new Point();
}
Tous les objets de la classe Point partageront la même
composante "origine" = (0, 0).
Variables statiques
• Si une variable est déclarée
"static", tous les objets de la classe partageront cette variable.
• Donc, toute modification d'une variable statique dans un objet quelconque de la classe est répercutée dans tous les objets de la classe.
Variables statiques
Point.origine
Comme la valeur d'une variable statique ne dépend que de la classe (et pas de l'objet choisi dans la classe), on peut accéder directement à cette valeur. Ainsi
désignera le point (0, 0).
Méthodes statiques
• Si une méthode est déclarée
"static", tous les objets de la classe partageront cette méthode.
• Une méthode statique n'a pas directement accès aux variables non statiques.
• On peut appeler une méthode statique par "Classe.Methode"
Math.sqrt
sqrt est une méthode statique de la classe Math
Tables
• Table = Ensemble de couples appelées entrées de la table.
• Entrée = (clef, information)
• Opérations sur les tables - création
- insertion
- modification d'une entrée - suppression
- recherche de l'information associée à une clé
Représentation d'une table
• Par un tableau
entrées : (rang, nom)
final static int N = 450;
static String[] PromoX = new String[N];
PromoX[187] = "Martin";
• Par deux tableaux
entrées : (nom, numéro de poste)
static String[] PromoX = new String[N];
static int[] Tel = new int[N];
• Par deux tableaux ordonnés
• Par hachage (cf plus loin)
• Par arbre (cf plus loin)
Représentation par un seul tableau
• création : O(n)
• insertion : O(n)
• recherche : O(1)
• correction : O(1)
• suppression : O(n)
C'est la recherche "en accès direct"
Recommandé pour de petites tables numériques.
Représentation par deux tableaux Entrées : (nom, numéro de poste) Les entrées ne sont pas triées !
static String[] PromoX = new String[N];
static int[] Tel = new int[N];
• création : O(n)
• insertion : O(1)
• correction : O(n)
• suppression : O(n)
• recherche : O(n)
(n/2 + o(n) en moyenne en cas de succès, n + o(n) en cas d'échec) C'est la recherche "séquentielle"
Recherche séquentielle
final static int N = 6;
static String Noms[] = new String[N+1];
static int tel[] = new int[N+1];
static int Recherche (String x) {
int i = 0;
Noms[N] = x;
tel[N] = -1;
while (! x.equals(Noms[i])) ++i;
return tel[i];
}
Représentation tableaux ordonnés entrées : (nom, numéro de poste)
static String[] Noms = new String[N];
static int[] Tel = new int[N];
On suppose les noms triés par ordre alphabétique.
• insertion : O(n)
(n/2 + o(n) en moyenne)
• correction : O(n)
• suppression : O(n)
• recherche : O(log n)
(en cas de succès et d'échec )
C'est la recherche "dichotomique"
Recherche dichotomique
static int RechercheDichotomique (String x) {
int i, g, d, comp;
g = 0;
d = N-1;
while (g <= d) {
i = (g + d) / 2;
comp = x.compareTo(Noms[i]);
if (comp == 0) return tel[i];
if (comp < 0) d = i - 1;
else
g = i + 1;
}
return -1;
}
Recherche par interpolation
• On suppose que les clés sont
numériques et que la distribution
des clés est uniforme sur l'intervalle [v0, vn] où
v0 = cles[0] et vn = cles[n]
• On recherche la clé v. La plus grande probabilité est qu'elle soit proche de cles[k], avec
k = n
• On itère le procédé d'interpolation
• Complexité moyenne inférieure à 1 + log log n
v - v0 vn - v0
Hachage
• Compromis Espace-Temps
(temps de recherche proportionnel au taux de remplissage de la table)
• Les clés ne sont pas triées
• Principe : la table est divisée en M tables (en accès séquentiel par exemple). Une fonction, dite de hachage
h : Clés → [0 .. M-1]
donne, pour chaque clé c, le numéro h(c) de la sous-table associée à c.
• Taux de remplissage : r = N/M
Exemple de hachage
Temps de recherche moyen*
Succès : (8.1 + 5.2 + 3.1)/14 = 1, 5 Echec : (2.0 + 3.1 + 4.2 + 1.3)/10 = 1,4
* Il faudrait ajouter le calcul de h(c)
25 32 43 51 67 89 20 11 13 53 57 59 14 15
5 2 3 1 7 9 0 1 3 3 7 9 4 5
0 (20, Martin)
1 (51, Pierre), (11, Sophie) 2 (32, Jean)
3 (43, Sylvie), (13, Robert), (53, Paul) 4 (14, Annie)
5 (25, Jacques), (15, Adeline) 6
7 (67, Catherine), (57, Danièle) 8
9 (89, Julie), (59, Philippe)
Temps de recherche moyen pour la résolution des
collisions par chainage
n éléments, table de taille m, r = n/m
Calcul du hachage : Ο(1)
Temps total moyen, calcul du hachage inclus:
Succès : 2 + r/2 - 1/2m Echec : 1 + r
Si r = 1,4 on trouve 2,7 et 2,4
Hachage à adressage ouvert
• Même principe, mais les tables sont constituées d'un seul élément.
Autrement dit, on suppose qu'il y a au plus N entrées à ranger, et on choisit M > N et une fonction de hachage
h : Clés → [0 .. M-1]
Soit c une clé. Si Table[h(c)] n'est pas occupé, on y range l'entrée associée à c. Sinon on la range dans la première place non occupée :
Table[h(c)+1], Table[h(c)+2], …
h(c) = c mod 20
Temps de recherche en cas de succès :
27/14
0 20 1
2 3 4
5 25 45 6 45 7 67
8
9 89 10
11 51 11 12 32
13 73 13 53
14 11 14
15 13
16 53
17 57
18 14
19 59
25 32 73 51 67 89 20 11 13 53 57 59 14 15 5 12 13 11 7 9 0 11 13 13 17 19 14 5
Complexité du hachage à adressage ouvert Si r = N/M est le taux de
remplissage, le temps moyen de recherche est au plus
• En cas de succès
• En cas d'échec
• Pour r = 2/3, on trouve 2 et 5, (indépendamment de N).
• Désastreux pour r proche de 1
Phénomènes de regroupement Le hachage simple produit des phénomènes de regroupement autour des clés qui sont insérées au premier essai.
Probabilité pour que la case i soit choisie à la prochaine insertion (en supposant que h(c) est distribuée uniformément)
P(1) = 2/14 P(4) = 3/14 P(5) = 1/14 P(10) = 5/14 P(11) = 1/14 P(13) = 2/14
0 1 2 3 4 5 6 7 8 9 10 11 12 13
Double hachage
Une solution pour éviter les phénomènes de regroupement est d'utiliser un double hachage :
On a donc deux fonctions de hachage
h : Clés → [0 .. M-1]
h' : Clés → [1 .. r]
Soit c une clé. Si Table[h(c)] n'est pas occupé, on y range l'entrée associée à c. Sinon on la range dans la première place non occupée :
Table[h(c)+h'(c)], ou à défaut Table[h(c)+2h'(c)], ou à défaut Table[h(c)+3h'(c)], etc.
Hachage quadratique
Une autre solution pour la résolution des collisions est de ranger l'entrée associée à c dans Table[h(c)] ou à défaut dans
Table[h(c) + 1], Table[h(c) + 4], Table[h(c) + 9],
Table[h(c) + 16], etc.
On démontre que le temps moyen de recherche est au plus
• En cas de succès
1 - ln(1-r) - r/2
• En cas d'échec
1/(1-r) - ln(1-r) - r
Complexité du double hachage On démontre (sous des hypothèses de distribution uniforme) que si r
= N/M est le taux de remplissage, le temps moyen de recherche pour un hachage double est au plus
• En cas de succès
-ln(1-r)/r
• En cas d'échec
1/(1-r)
• Pour r = 9/10, on trouve 2,56 et 10 (indépendamment de N).
• Pour r = 99/100, on trouve 4,65 et 100 (indépendamment de N).
Choix des fonctions de hachage
• Paradoxe des anniversaires : si on prend une fonction de hachage
h : [1..23] → [1 .. 365]
au hasard, la probabilité pour que deux clés (au moins) aient la même image par h est > 1/2 (!)
• Il est donc difficile de trouver une fonction injective de [0..N-1]
dans [0..M-1]
• Un choix classique est h(c) = c mod M
où M est un nombre premier.
Choix des fonctions de hachage (2)
• Pour des clés alphabétiques, on peut choisir
(c[1]Bn-1 + c[2]Bn-2 + … + c[n]) mod N
où B = 256 et N est premier
Exemple c = PASCAL, N = 143 h(c) = 80. 264 + 65. 232 + 83. 224 + 67. 216 + 65. 28 + 76 mod 143
• Pour les fonctions de hachage secondaires, on peut prendre
h'(c) = 8 - (c mod 8) ou h'(c) = c mod (N-1)
Choix des fonctions de hachage (3)
• Autre choix efficace
[
(c[1]Bn-1 + … + c[n]) mod 2w]
mod Noù B = 131 et w est la longueur du mot machine.
En effet, Bi a un cycle maximal pour 8 ≤ k ≤ 64.
Les conseils de la semaine…
• Penser aux techniques de hachage!
• Ne jamais remplir une table de hachage à plus de 90%
• Avant d'utiliser un double hachage, faire un "devis"