Planche 1

Cours 3
Structures de données dynamiques

Jean-Jacques.Levy@inria.fr
http://jeanjacqueslevy.net
tel: 01 39 63 56 89

secrétariat de l'enseignement:
Catherine Bensoussan
cb@lix.polytechnique.fr
Aile 00, LIX
tel: 01 69 33 34 67

http://www.enseignement.polytechnique.fr/informatique/


Planche 2

Plan

  1. Listes -- Arbres
  2. Opérations de base
  3. Polymorphisme?
  4. Abstraction1: représentation des ensembles
  5. Abstraction2: paquetage de grands nombres
Répertoire pour stocker ses TD

/users/profs/info/TD/tc/g/n sur poly.polytechnique.frg Î {1, 12} est le numéro de groupe et n Î {1, 2, 3, ... 10} le numéro du cours. Ne pas oublier de mettre son nom dans le nom du fichier. (Répertoire en accès en écriture pour tout le monde)


Planche 3

Listes
Comment représenter des
ensembles dynamiques? Dont le nombre d'éléments peut varier dans le temps? Les tableaux ont leur dimension fixée à leur création. Il faut considérer le nombre maximal de leurs éléments Þ gachis (en place mémoire).

Symétriquement, comment représenter des
vecteurs (ou matrices) creux? Un tableau prend l'espace mémoire nécessaire pour toutes les valeurs de l'indice, et donc pour les valeurs inutiles.

D'où la structure de données des
listes dont les éléments sont les éléments non consécutifs de l'ensemble (ou des paires [indice, valeur] dans le cas des vecteurs ou matrices creux). On ne peut accèder que séquentiellement aux éléments d'une liste.



Planche 4

Représentation des listes


class Liste {
  int hd; Liste tl;

  Liste (int v, Liste a) {
     hd = v; tl = a;
  }
}
ou

class Liste {
  int indice; int valeur; Liste suivant;

  Liste (int i, int v, Liste a) {
     indice = i; valeur = v; suivant = a;
  }
}
Exercice 1Pour les fous de l'optimisation de l'espace-mémoire, il existe des représentations encore plus compactes des listes, e.g. hash-consing. Savez-vous en trouver une?


Planche 5

Arbres


Les arbres représentent des ensembles dynamiques (parfois ordonnés) avec
accès rapide aux éléments (en O(log n) pour n éléments).

Les arbres représentent aussi les
termes des structures algébriques (cf. plus tard), et sont donc la structure de base de tous les systèmes de calcul formel (compilation, démonstration automatique, Maple).



Planche 6

Opérations courantes sur les listes


Deux styles de programmation

En Java, les types des données ne permettent pas de distinguer entre ces deux styles de programmation (toute variable étant a priori modifiable).

L'inefficacité des listes non modifiables est contre-balancée par l'existence d'un
garbage collector, ie un glâneur de cellules qui récupère régulièrement les objets non utilisés. Cet effet est d'autant plus fort que le GC est efficace (en temps).

En majeure 1, les techniques de GC sont étudiées dans le cours Langages de programmation


Planche 7

Concaténation non destructive -- Append


static Liste append (Liste a, Liste b) {
  if (a == null) return b;
  else return new Liste (a.hd, append (a.tl, b)) ;
}
Récurrence structurelle sur le premier argument. En O(n).



Planche 8

Concaténation destructive -- nConc


static Liste nConc (Liste a, Liste b) {
  if (a == null) return b;
  else {
    a.tl = nConc (a.tl, b);
    return a;
  }

Récurrence structurelle sur le premier argument. En O(n).

Modification de la valeur du premier argument de
nConc.

Programmation dangereuse, car création implicite d'
alias.


Planche 9

Exemples sur listes - suite


static Liste nConcI (Liste a, Liste b) {
  if (a == null) return b;
  else {
    Liste c = a;
    while (c.tl != null) 
      c = c.tl;
    c.tl = b;
    return a;
  }
}
On combine les inconvénients: itératif et destructif! En O(n).


Planche 10

Image mirroir -- version non destructive

static Liste reverse (Liste x) {
  if (x == null) return null;
  else return append (reverse (x.tl), new Liste (x.hd, null));
}

static Liste mirroir (Liste x) { return mirroir1 (x, null); }

static Liste mirroir1 (Liste x, Liste r) {  // r accumulateur
  if (x == null) return r;
  else return mirroir1 (x.tl, new Liste(x.hd, r));
}

Récursivité terminale dans mirroir1 Þ version itérative

static Liste mirroirI (Liste x) {
  Liste r = null;
  for (; x != null; x = x.tl)
    r = new Liste (x.hd, r);
  return r;
}

Planche 11

Image mirroir -- version destructive

static Liste nReverse (Liste x) {
  if (x == null) return null;
  else {
    Liste y = nReverse (x.tl);
    x.tl = null;
    return nConc (y, x);
  }
}

static Liste nReverseI (Liste x) {
  Liste r = null;
  while (x != null) {
    Liste y = x.tl; x.tl = r; r = x; x = y;
  }
  return r;
}
Exercice 2Calculer la complexité de ces différentes fonctions.

Exercice 3Passer du récursif à l'itératif avec un accumulateur comme pour mirroir.


Planche 12

Autres fonctions

static Liste listeAleatoire (int n) {
  Liste r = null;
  for (int i = 0; i < n; ++i)
     r = new Liste ((int) (Math.random() * 100), r);
  return r;
}

static void imprimer (Liste x) {
  for (Liste y = x; y != null; y = y.tl) 
    System.out.print (y.hd + " ");
  System.out.println ();
}

static Liste lireDansTableau (String[] a) {
  Liste r = null;
  for (int i = a.length - 1; i >= 0; --i)
    r = new Liste (Integer.parseInt (a[i]), r);
  return r;
}

Planche 13

Autres fonctions -- bis

static boolean member (int n, Liste x) {
  if (x == null) return false;
  else return n == x.hd || member (n, x.tl);
}

static boolean equal (Liste x, Liste y) {
  if (x == null) return y == null;
  else if (y == null) return x == null;
  else return x.hd == y.hd && equal (x.hd, y.hd);
}

static boolean eq (Liste x, Liste y) {
  return x == y;
}
Exercice 4Décrire la différence entre equal et eq.


Planche 14

Arbres -- représentation des arbres


class Arbre {
  int val; Arbre fG, fD;

  Arbre (int v, Arbre a, Arbre b) {
    val = v; fG = a; fD = b;
  }
}
Si l'arbre n'est pas binaire, plusieurs solutions avec des tableaux

class Arbre {
  int val; Arbre[] fils;
  Arbre (int v, Arbre[] a) { val = v; fils = a; }
}
ou avec des listes


class Arbre {
  int val; ListeArbres fils;
  Arbre (int v, ListeArbres x) { val = v; fils = x; }
}

class ListeArbres {
  Arbre hd; ListeArbres tl;
  ListeArbres (Arbre a, ListeArbres x) { val = v; fils = x; }
}

Planche 15

Fonctions sur les arbres


static void imprimer (Arbre a) {
  if (a != null) {
    System.out.print ("["); imprimer (a.fG);
    System.out.print (" " + a.val + " ");
    imprimer (a.fD); System.out.print ("]"); 
  }
}
donne [ [ 3 ] 5 [ [ 22 ] 37 [ 40 ] ] ]


Planche 16

Fonctions sur les arbres


static int next;

static Arbre lireDansTableau (String[] s) {
    if (!s[next].equals ("[") ) 
      return null;
    else {
      ++next; Arbre a = lireDansTableau (s);
      int v = Integer.parseInt (s[next]);
      ++next; Arbre b = lireDansTableau (s);
      if (!s[next++].equals ("]") ) {
        System.err.println ("Mauvais arguments."); 
        System.exit (1);
      }
      return new Arbre (v, a, b);
    }
}
Exercice 5Ecrire un programme qui compte le nombre de feuilles nf et de noeuds internes ni.

Exercice 6Vérifier que nf = ni + 1 si l'arbre est binaire (ie. tout noeud interne a deux fils).


Planche 17

Fonctions sur les arbres

static Liste listeDesNoeuds (Arbre a) {
  if (a == null) return null;
  else return append (listeDesNoeuds (a.fG), 
                      append (new Liste (a.val, listeDesNoeuds (a.fD))));
}
Exercice 7Faire une version plus efficace et esthétique.

Exercice 8Ecrire une fonction arbreDeLaListe transformant une liste en un arbre (aussi équilibré que possible).

static boolean member (int n, Arbre x) {
  if (x == null) return false;
  else return n == x.val || member (n, x.fG) || member (n, x.fD);;
}

static boolean equal (Arbre x, Arbre y) {
  if (x == null) return y == null;
  else if (y == null) return x == null;
  else return x.val == y.val && equal (x.fG, y.fG) && equal (x.fD, y.fD);;
}

Planche 18

Des listes baroques

Autrefois, l'espace mémoire et la vitesse étaient très critiques. Toutes sortes d'optimisations avaient lieu.

On peut combiner ces 3 optimisations souvent bien inutiles et aussi très laides.


Planche 19

Listes / Arbres polymorphes?

Bref, Java n'a pas de types de données polymorphes. (cf. Generic Java, http://www.cs.bell-labs.com/who/wadler/pizza/gj/, par Odersky et Wadler)


Planche 20

Listes / Arbres polymorphes


class Liste {
  Object hd; Liste tl;
  Liste (Object v, Liste a) {
     hd = v; tl = a;
  }

  static Liste append (Liste a, Liste b) {
    if (a == null) return b;
    else return new Liste (a.hd, append (a.tl, b)) ;
  }

  public static void main (String[] args) {
    Liste x = new Liste(new Integer(2), new Liste( new Integer (3), null));
    Liste y = new Liste(new Integer(4), new Liste( new Integer (5), null));
    Liste z = append (x, y);
    int u = ((Integer) z.hd).intValue();
    System.out.println (u);
  }
}

Exercice 9Faire mieux?


Planche 21

Abstraction -- Encapsulation

Par exemple, comment représenter un ensemble dont le nombre d'éléments peut évoluer dynamiquement?

Par une liste, par un arbre?

Quelles sont les opérations à réaliser sur les ensembles? A fournir dans l'
interface ou encore API (application program interface)

On veut donc construire un module avec certaines fonctions à exporter. Tout le reste est à cacher, car il s'agit de la représentation interne de notre abstraction.



Planche 22

Représentations d'ensembles


Planche 23

Listes ordonnées


class Ensemble {
  hd: int; tl: Ensemble;
  Ensemble (int x, Ensemble e) { hd = x; tl = e; }

static boolean vide (Ensemble e) { return e == null; }

static Ensemble ajouter (int x, Ensemble e) {
  if ( e == null || x < e.hd ) return new Ensemble (x, e);
  else if (x == e.hd) return e;
  else if (x > e.hd) return new Ensemble (e.hd, ajouter (x, e.tl));
}

static boolean appartient (int x, Ensemble e) {
  if ( e == null || x < e.hd ) return false;
  else return x == e.hd || appartient (x, e.tl);
}

static Ensemble enlever (int x, Ensemble e) {
  if ( e == null || x < e.hd ) return e;
  else if (x == e.hd) return e.hd;
  else if (x > e.hd) return new Ensemble (e.hd, enlever (x, e.tl));
}

Planche 24

Listes et ensembles

Exercice 10Calculer l'union, l'intersection, la différence symétrique.

Exercice 11Donner des versions destructives de ces fonctions.

Exercice 12Donner des versions itératives et comparer?

Exercice 13Refaire ces programmes avec la représentation par des listes non ordonnées. Quels en sont les coûts?

Exercice 14Comment passer d'une représentation à l'autre?

Exercice 15Faire le tri par fusion sur les listes non ordonnées.


Planche 25

Entiers en précision arbitraire

Un cas classique d'utilisation des listes est de faire un paquetage d'opérations arithmétiques en précision arbitraire, ie au delà de
231 - 1.

Représentation d'un grand nombre. On suppose une base
B donnée. Par exemple B=10, mais plus vraisemblablement B=230.

Nous prenons petit endien.

On fait facilement l'addition et la multiplication, la lecture et l'impression. Comment faire la division et le test de primalité, utiles pour faire de la cryptographie?



Planche 26

Grands nombres et division

On veut diviser
u par v, ie u = q v + r0 £ r < v. D'abord, on voit que toute la difficulté est de traiter le cas où u/v < B. On fera ensuite l'opération chiffre par chiffre du quotient.

Quand
u/v < B, une bonne approximation du quotient s'obtient en regardant les 2 premiers chiffres de u et le premier chiffre de v, notamment quand le premier chiffre v1 de v vérifie v1 ³ ë B/2 û.

En effet, posons
u = u0 u1...un et v = v1...vn en base B. Calculons q' = min (ë (u0*B + u1)/v1 û, B-1 ). Alors

Lemme 1:
q' ³ q
Lemme 2: Si
v1 ³ ë B/2 û, alors q'-2 £ q £ q'


Planche 27

DIVISION DE u PAR v (Knuth vol2, p.272)

Soient
u = u1 u2 ... um+n et v = v1...vn Calculer ë u/v û = q0 q1...qm et u mod v = r1 ... rn


Planche 28

Cryptographie à clé publique

Eviter le partage des secrets. La méthode RSA utilise un système à 2 clés: une publique
e et une autre secrète d. (Cf.  le cours Morain en majeure Info ou le livre ``Applied Cryptography'' de Schneier.)

Soit n=pqp, q sont premiers. On encode avec une clé e première avec (p-1)(q-1). On décode avec une clé d telle que
ed º 1 mod (p-1)(q-1)
On suppose les messages m coupés en morceaux mi < n, et on fait
ci = mie mod n
On décode par
mi = cid mod n
On vérifie que
cid º mied º mi mod n

On oublie donc p et q. La clé e est publique. Typiquement, pq a une centaine de chiffres décimaux.


Planche 29

Grands nombres premiers

Il faut donc trouver de grands nombres premiers
p, q. (50 à 100 chiffres). Pour aller vite, on fait des tests probabilistes pour savoir si p est premier:

Méthode 1 (Lehmann)

  1. Choisir un nombre aléatoire a tel que a < p

  2. Calculer a(p-1)/2 mod p

  3. Si a(p-1)/2 ¬ º1  ou  -1, répondre NON.

  4. Sinon p est premier à 50%.
(On itère donc pour 10 valeurs de a).


Planche 30

Méthode 2 (Rabin-Miller, 1980)

Soit
b la plus grande puissance de 2 telle que p=1+2b   m

  1. Choisir un nombre aléatoire a tel que a < p

  2. Poser j=0 et z= am mod p

  3. Si z=1 ou z=p-1, alors répondre OUI.

  4. Si j>0 et z=1, alors répondre NON.

  5. Faire j=j+1. Si j<b et si z=z2 mod p ¹ p-1, revenir en (4). Si z=p-1, alors répondre OUI.

  6. Si j=b et z = p-1 alors répondre NON.
Sur un nombre de 256 bits, se tromper après 6 tests est inférieur à 1/251.




This document was translated from LATEX by HEVEA.