algorithme et structure de données iup1 miage. tableau à une dimension tableau de trois...
TRANSCRIPT
Algorithme et structure de données
IUP1 Miage
Tableau à une dimension
Tableau de trois caractères
{’u’, ’b’, ’c’}
t[0] t[1] t[2]
char[] t={’u’, ’b’, ’c’};
Tableau de trois entiers
int[] t={4, 100, 21};
Déclaration d’un tableau
// déclaration
char[] t ;
// création sans initialisation
t=new char[3] ;
// création avec initialisation
t={’u’,’b’,’c’};
Parcourir un tableau
char[] t = new char[3] ;
for(int i=0 ; i< t.length ; i++){
t[i]=Console.readChar(”? ”);
}
for(int i=0 ; i< t.length ; i++){
t[i]=t[i]+1;
}
for(int i=0 ; i< t.length ; i++){
S.o.p(t[i]);
}
Cryptographie : Codage de César
Discipline incluant les principes, les moyens et les méthodes de transformation des données, dans le but de masquer leur contenu, d'empêcher leur modification ou
leur utilisation illégale
Décaler les lettres de l'alphabet de trois crans vers la gauche
ABCDEFGHIJKLMNOPQRSTUVWXYZDEFGHIJKLMNOPQRSTUVWXYZABC
AVE CAESAR
Cryptographie : Codage de César
Discipline incluant les principes, les moyens et les méthodes de transformation des données, dans le but de masquer leur contenu, d'empêcher leur modification ou
leur utilisation illégale
Décaler les lettres de l'alphabet de trois crans vers la gauche
ABCDEFGHIJKLMNOPQRSTUVWXYZDEFGHIJKLMNOPQRSTUVWXYZABC
AVE CAESARDYH FDHVDU
Crypter un message
void crypter(char [] mess) {
?????
}
void deCrypter(char [] mess) {
?????
}
Crypter un message
void crypter(char [] mess) {
char aux ;
for(int i=0; i<mess.length; i++) {
aux = (char) (mess[i]+3) ;
if (aux > 'z') aux=(char)(aux - 26);
mess[i]=aux;
}
}
Décrypter un message
void deCrypter(char [] mess) {
char aux ;
for(int i=0; i<mess.length; i++) {
aux = (char)(mess[i]-3) ;
if (aux < 'a') aux=(char)(aux + 26);
mess[i]=aux;
}
}
Crypter / décrypter …
Chiffrer le message "OUI" avec un décalage de 10
Chercher un élément dans un tableau
int indexOf(char[] t, char c){
int i=0;
while(i<t.length && t[i]!=c) i++;
if (i==t.length)
return -1;
else
return i;
}
Tableau à deux dimensions
Notion mathématique de matrice Tableau de tableaux
u b c t[0][0] t[0][1] t[0][2]
x y z t[1][0] t[1][1] t[1][2]
a z e t[2][0] t[2][1] t[2][2]
Déclaration d’un tableau
// déclaration
char[][] t ;
// création sans initialisation
t=new char[3][3] ;
// création avec initialisation
t={{’u’,’b’,’c’},
{’x’,’y’,’z’},
{’a’,’z’,’e’}} ;
Parcourir un tableau
char[][] t = new char[3][3] ;
for(int l=0 ; l<3 ; l++){
for(int c=0 ; c<3 ; c++){
t[l][c]=Console.readChar(”? ”);
}
}
A B C D E F G H I J K L M N O P Q R S T U V W X Y Z
A A B C D E F G H I J K L M N O P Q R S T U V W X Y Z
B B C D E F G H I J K L M N O P Q R S T U V W X Y Z A
C C D E F G H I J K L M N O P Q R S T U V W X Y Z A B
D D E F G H I J K L M N O P Q R S T U V W X Y Z A B C
E E F G H I J K L M N O P Q R S T U V W X Y Z A B C D
F F G H I J K L M N O P Q R S T U V W X Y Z A B C D E
G G H I J K L M N O P Q R S T U V W X Y Z A B C D E F
H H I J K L M N O P Q R S T U V W X Y Z A B C D E F G
I I J K L M N O P Q R S T U V W X Y Z A B C D E F G H
J J K L M N O P Q R S T U V W X Y Z A B C D E F G H I
K K L M N O P Q R S T U V W X Y Z A B C D E F G H I J
L L M N O P Q R S T U V W X Y Z A B C D E F G H I J K
M M N O P Q R S T U V W X Y Z A B C D E F G H I J K L
N N O P Q R S T U V W X Y Z A B C D E F G H I J K L M
O O P Q R S T U V W X Y Z A B C D E F G H I J K L M N
P P Q R S T U V W X Y Z A B C D E F G H I J K L M N O
Q Q R S T U V W X Y Z A B C D E F G H I J K L M N O P
R R S T U V W X Y Z A B C D E F G H I J K L M N O P Q
S S T U V W X Y Z A B C D E F G H I J K L M N O P Q R
T T U V W X Y Z A B C D E F G H I J K L M N O P Q R S
U U V W X Y Z A B C D E F G H I J K L M N O P Q R S T
V V W X Y Z A B C D E F G H I J K L M N O P Q R S T U
W W X Y Z A B C D E F G H I J K L M N O P Q R S T U V
X X Y Z A B C D E F G H I J K L M N O P Q R S T U V W
Y Y Z A B C D E F G H I J K L M N O P Q R S T U V W X
Z Z A B C D E F G H I J K L M N O P Q R S T U V W X Y
Créer le tableau alphabet
char[][] alphabet =new char[26][26] ;
void initAlphabet(){ char debut='a', lettre ; for(int ligne=0;ligne<26;ligne++){ ???; for(int colonne=0;colonne<26;colonne++) { alphabet[ligne][colonne]= ???; if (lettre<'z') ???; else ???; } ???; }}
Créer le tableau alphabet
char[][] alphabet =new char[26][26] ;
void initAlphabet(){ char debut='a', lettre ; for(int ligne=0;ligne<26;ligne++){ lettre=debut; for(int colonne=0;colonne<26;colonne++) { alphabet[ligne][colonne]=lettre; if (lettre<'z') lettre ++; else lettre='a'; } debut++; }}
Carré de Vigenère (1525-1596)
Pour coder - ou décoder - un texte, on utilise– un mot clef– le tableau carré constitué de 26 alphabets
décalés
Et on code lettre à lettre : chiffré = clair + clef
A B C D E F G H I J K L M N O P Q R S T U V W X Y Z
A A B C D E F G H I J K L M N O P Q R S T U V W X Y Z
B B C D E F G H I J K L M N O P Q R S T U V W X Y Z A
C C D E F G H I J K L M N O P Q R S T U V W X Y Z A B
D D E F G H I J K L M N O P Q R S T U V W X Y Z A B C
E E F G H I J K L M N O P Q R S T U V W X Y Z A B C D
F F G H I J K L M N O P Q R S T U V W X Y Z A B C D E
G G H I J K L M N O P Q R S T U V W X Y Z A B C D E F
H H I J K L M N O P Q R S T U V W X Y Z A B C D E F G
I I J K L M N O P Q R S T U V W X Y Z A B C D E F G H
J J K L M N O P Q R S T U V W X Y Z A B C D E F G H I
K K L M N O P Q R S T U V W X Y Z A B C D E F G H I J
L L M N O P Q R S T U V W X Y Z A B C D E F G H I J K
M M N O P Q R S T U V W X Y Z A B C D E F G H I J K L
N N O P Q R S T U V W X Y Z A B C D E F G H I J K L M
O O P Q R S T U V W X Y Z A B C D E F G H I J K L M N
P P Q R S T U V W X Y Z A B C D E F G H I J K L M N O
Q Q R S T U V W X Y Z A B C D E F G H I J K L M N O P
R R S T U V W X Y Z A B C D E F G H I J K L M N O P Q
S S T U V W X Y Z A B C D E F G H I J K L M N O P Q R
T T U V W X Y Z A B C D E F G H I J K L M N O P Q R S
U U V W X Y Z A B C D E F G H I J K L M N O P Q R S T
V V W X Y Z A B C D E F G H I J K L M N O P Q R S T U
W W X Y Z A B C D E F G H I J K L M N O P Q R S T U V
X X Y Z A B C D E F G H I J K L M N O P Q R S T U V W
Y Y Z A B C D E F G H I J K L M N O P Q R S T U V W X
Z Z A B C D E F G H I J K L M N O P Q R S T U V W X Y
Clair HELLOWORLDClef ECSECSECSEChiffré LGDPQOSTDH
Carré de Vigenère (1525-1596)
Clair HELLOWORLDClef ECSECSECSEChiffré LGDPQOSTDH
Crypter/décrypter un message
void crypter(char[] mess, char[] clef) {
int l, c ;
for(int i=0; i<mess.length; i++) {
???
}
}
void deCrypter(char[] mess, char[] clef) {
for(int i=0; i<mess.length; i++){
???
}
}
Crypter un message
void crypter(char[] mess, char[] clef) {
int l, c ;
for(int i=0; i<mess.length; i++) {
l=clef[i]-'a';
c=mess[i]-'a';
mess[i]=alphabet[l][c];
}
}
Décrypter un message
void deCrypter(char[] mess, char[] clef) {
for(int i=0; i<mess.length; i++){
mess[i]=(char)('a'+
indexOf(tabApha[clef[i]-'a'],mess[i]));
}
}
Codage de Hill
Coder simultanément des groupes de 2 lettres! Remplacer chaque lettre par son ordre dans l'alphabet :
A devient 0, B devient 1,..., Z devient 25 Calculer pour chaque bloc de 2 nombres à coder x1x2,
les combinaisons linéaires : y1 = ax1+bx2y2 = cx1+dx2
Ramener les résultats y1 et y2 dans l’intervalle 0..25 en prenant leur reste dans la division par 26
Transformer y1 et y2 en lettres
Le choix de la clé correspond ici au choix des quatre entiers a,b,c,d
Codage de Hill (main)
void main(String str []) { // on suppose que le message a un nombre pair de caracteres char[] message= {'e','l','e','c','t','i','o','n'} ; int[] clef = {1,1,5,1}; if (! clefValide(clef)) S.o.p("LA CLEF N'EST PAS VALIDE !"); else { S.o.p("MESSAGE EN CLAIR : ");S.o.p(message); crypter(message,clef); S.o.p("MESSAGE CRYPTER : ");S.o.p(message); deCrypter(message,clef); S.o.p("MESSAGE DECRYPTER : ");S.o.p(message); } }
Codage de Hill (crypter)
void crypter(char[] message, int[] clef) {
int taille=message.length ;
int[] x = new int[taille] ; int[] y = new int[taille];
for(int i=0; i<taille; i++) x[i]=message[i]-'a';
for(int i=0; i<taille/2; i++) {
y[2*i] =(clef[0]*x[2*i]+clef[1]*x[2*i+1])%26 ;
y[2*i+1]=(clef[2]*x[2*i]+clef[3]*x[2*i+1])%26 ;
}
for(int i=0; i<taille; i++) message[i] =(char) (y[i]+'a');
}
Codage de Hill (decrypter)
boolean clefValide(int[] clef){ int det=determinant(clef) ; // une clef est valide ssi
// son determinant est premier avec 26 return (pgcd(det,26)==1) ;}
void deCrypter(char[] message, int[] clef) { crypter(message, inverserClef(clef)); }
Codage de Hill (decrypter)
int[] inverserClef(int[] clef) { int[] iClef= new int[clef.length]; int det =determinant(clef) ; int idet=iDeterminant(clef); iClef[0]=(clef[3]*idet)%26 ; // obtenir une valeur dans l'intervalle [0..25] int d=-clef[1]*idet ; while (d<0) {d=d+26;} iClef[1]=d%26; // obtenir une valeur dans l'intervalle [0..25] d=-clef[2]*idet ; while (d<0) {d=d+26;} iClef[2]= d%26; iClef[3]=(clef[0]*idet)%26 ; return iClef ; }
Codage de Hill (decrypter)
int iDeterminant(int[] clef){
int d=determinant(clef);
// calculer l'inverse de d dans Z/26Z
// c'est a dire trouver un nombre id
// tel que d*id=1+k*26
int id=1 ; while ((d*id)%26 !=1) id++;
return id ;
}
Voici mon tableau [I@18c3679 !!!!
void main(String[] args) {int[] tab = {5, 2, 6};S.o.p("Voici mon tableau" + tab);
}
[ --> TableauI --> integer@ --> Adresse18c3679 adresse en hexadécimal
Affection de tableaux
void main(String[] args){
int[] t1 = {5, 2, 6};
int[] t2;
t2 = t1;
S.o.p("Tableaux "+t1+","+t2);
}
Affection de tableaux
void main(String[] args){
int[] t1 = {5, 2, 6};
int[] t2;
t2 = t1;
S.o.p("Tableaux "+t1+","+t2);
}
Tableaux [I@18c3679 , [I@18c3679
1 tableau ou 2 tableaux ?
void main(String[] args){
int[] t1 = {5, 2, 6};
int[] t2 = t1;
t2[1] = 8;
S.o.p(t1[1]);
}
1 tableau ou 2 tableaux ?
void main(String[] args){
int[] t1 = {5, 2, 6};
int[] t2 = t1;
t2[1] = 8;
S.o.p(t1[1]);
}
8
Egalité de tableaux !
void main(String[] args){int[] t1 = {5, 2, 6};int[] t2 = new int[t1.length];for(byte i = 0; i < t1.length; i++){
t2[i] = t1[i];S.o.p(t1[i]+" ? "+t2[i]);
}S.o.p(t1 == t2);
}
Egalité de tableaux !
void main(String[] args){int[] t1 = {5, 2, 6};int[] t2 = new int[t1.length];for(byte i = 0; i < t1.length; i++){
t2[i] = t1[i];S.o.p(t1[i]+" ? "+t2[i]);
}S.o.p(t1 == t2);
}
5 ? 5 2 ? 2 6 ? 6false
Trier un tableau …
Réorganiser une collection d'objets selon un ordre déterminé– Les objets à trier font partis d'un ensemble muni
d'une relation d'ordre total
Méthode de tri indépendamment de la fonction d'ordre– la seule opération nécessaire est de pouvoir
comparer tout couple d'objets
Caractéristiques d'un tri
Complexité algorithmique dans le pire des cas : le nombre d'opérations effectuées dans le pire des cas
pour trier un ensemble de n éléments
Complexité en moyenne : le nombre d'opérations effectuées en moyenne pour trier
un ensemble de n éléments
Comment représenter la complexité ?
Soit n la taille du tableau à trier Complexité en O(n) (complexité linéaire) :
il faut 2 fois plus de temps pour trier n*2 éléments que n
il faut 10 fois plus de temps pour trier n*10 éléments que n
Complexité en O(n²)il faut 2² fois plus de temps pour trier n*2 éléments que n
il faut 10² fois plus de temps pour trier n*10 éléments que n
O(n) << O(n log n) << O(n²)
Caractéristiques d'un tri
Mémoire nécessaire :hormis la mémoire nécessaire pour stocker les éléments
à trier, l'algorithme nécessite-t-il une quantité de mémoire supplémentaire dépendant du nombre d'éléments à trier ?
Stabilité :
lorsque deux éléments sont équivalents pour la relation d'ordre, conserver l'ordre dans lequel ces deux éléments se trouvaient avant le tri
Tri par Insertion
Principe : – c'est le tri que l’on utilise quand on a des dossiers à
classer– On prend un dossier et on le met à sa place parmi les
dossiers déjà triés– Puis on recommence avec le dossier suivant
Pour procéder à un tri par insertion, il suffit de …
Parcourir le tableau : on prend les éléments dans l'ordre
Comparer chaque élément avec les éléments précédents jusqu'à trouver sa place
Décaler les éléments du tableau pour insérer l'élément considéré à sa place dans la partie déjà triée
Tri par Insertion : exemple
Original : 3 9 6 1 2
insérons 9 : 3 9 6 1 2
insérons 6 : 3 6 9 1 2
insérons 1 : 1 3 6 9 2
insérons 2 : 1 2 3 6 9
Tri par insertion (algorithme)
void main(String[] args){
int[] tab={2,7,1,8,3,0,1};
afficherTableau(tab);
triInsertion(tab);
afficherTableau(tab);
}
Tri par insertion (version 1)
void triInsertion(int[] t){ int taille=t.length ,j , aux ; for(int i=1;i<taille;i++){
// on commence à 1 // inserer t[i] dans t[0..i-1] j=0 ; while (t[j]<t[i]) j++ ; // t[j]>=t[i] // decalage vers la droite sur t[j-1..i] aux = t[i]; for(int k=i;k>j;k--) t[k]=t[k-1]; t[j]=aux ; }}
Propriétés du Tri par Insertion
Ne nécessite par de connaître (stoker) tous les éléments des le début
Soit n le nombre d’éléments à trier
Le nombre de comparaisons nécessaires est de l'ordre de n²/4
Le nombre moyen d'échanges est de l'ordre de n²/2
Le pire cas est obtenu avec un tableau en ordre inverse
le meilleur cas avec un tableau déjà ordonné
Intéressant dans le cas où le tableau est déjà presque ordonné
Décalages en même temps que la recherche du point d’insertion
void triInsertion2(int[] t){
int taille=t.length, j , aux ;
for(int i=1;i<taille;i++) { //on commence à 1
aux = t[i];
j=i-1 ;
while (j>=0 && t[j]>aux) {t[j+1]=t[j];j--;}
// j==-1 || t[j]<=aux
t[j+1]=aux;
}
}
Tri par insertion en réalisant des échanges (version 3)
void triInsertion3(int[] t){ int taille=t.length, j , aux ; for(int i=1;i<taille;i++){ //on commence à 1 aux = t[i]; j=i-1 ; while (j>=0 && t[j]>aux) {echanger(t,j+1,j);j--;} } }
Tri par sélection
Rechercher le plus petit que l'on va replacer à sa position finale c'est-à-dire en premier,
puis rechercher le second plus petit que l'on va replacer également à sa position finale c'est-à-dire en second,
etc, jusqu'à ce que le tableau soit entièrement trié
Tri par sélection : exemple
Original : 3 9 6 1 2
plus petit est 1 : 1 9 6 3 2
plus petit est 2 : 1 2 6 3 9
plus petit est 3 : 1 2 3 6 9
plus petit est 6 : 1 2 3 6 9
Propriétés du tri par sélection
Soit n le nombre d’éléments à trier
Nombre de comparaisons nécessaires pour un tri est de n(n-1)/2
Nombre d'échanges est de l'ordre de n
Sélectionner pour trierpublic static void main(String[] args){
int[] tab = {-2, 0, 8 ,7 ,1, -5, 12, 10, 25, 5} ; final int N=tab.length ; int indMin ; int aux ;// Trier tab[0..N-1]for(int k=0 ; k<N-1 ; k++){
// chercher la place IndMin du plus petit élément// du sous tableau tab[k..N-1]indMin=k ;for(int j=k+1 ; j<N ; j++) {
if(tab[j] < tab[indMin]) indMin=j;}// échanger tab[indMin] avec tab[k]aux = tab[indMin];tab[indMin] = tab[k];tab[k] = aux;
}}
Comparaison des tris
Les tris par Sélection et par Insertion ont une efficacitéSemblable
Ils ont chacun une boucle externe qui parcourt tous leséléments, et une boucle interne qui compare les valeurs dela boucle externe avec presque toutes les valeurs de la liste
Donc approximativement n2 comparaisons sont nécessairespour trié une liste de taille n
Ces tris sont de l’ordre de n2
D’autres tris sont plus efficace : ordre de n log2 n
Tri à Bulles (bubble sort)
L'idée est de parcourir le tableau autant fois que nécessaire en échangeant 2 à 2 les éléments consécutifs qui ne sont pas dans le bon ordre.
c'est-à-dire si t[j-1] > t[j], alors on échange t[j-1] et t[j]
Tri à Bulles : les éléments les plus légers remontent vers la surface
Après le premier passage, le plus grand élément se trouve rangé en haut du tableau, on peut donc l'exclure dans l'itération suivante
Le même raisonnement s'applique sur les itérations suivantes : à chaque itération on diminue de 1 la taille de la partie à traiter du tableau
Plusieurs élément du « haut » du tableau peuvent se trouver rangés dans l'ordre ; on peut donc optimiser le traitement en s'arrêtant au dernier élément déplacé de l'itération précédente, ce qui peut être fait en ajoutant l'instruction
Tri à Bulles : exemple
23 10 75 5 37 49 53
10 23 75 5 37 49 53
10 23 5 75 37 49 53
10 23 5 37 75 49 53
10 23 5 37 49 75 53
10 23 5 37 49 53 75
Tri Bulles
void triBulles(int[] t){ int haut=t.length-1, while (haut>0) { for(int j=0 ; j<haut ; j++){
if(t[j]>t[j+1]) {echanger(t, j, j+1);} } haut--; }}
Permuter 2 éléments dans un tableau
void echanger(int[] t,int i,int j){
int aux =t[i];
t[i]=t[j];
t[j]=aux;
}
Complexité du tri à bulles
Pour chaque position, il faut parcourir en moyenne la moitié du tableau (au début tout le tableau, puis tout moins un élément, puis tout moins 2 éléments, puis ..., puis les 3 derniers éléments, puis les 2 dernières)
Ce qui nous fait n parcours, avec pour chaque parcours n / 2 opérations : au total, n² / 2 opérations (un petit peu moins si l'algorithme s'arrête avant), soit une complexité O (n²)
Tri par comptage de fréquence
int[] tab={2,5,5,7,9,2,3,2,0,9};
// on suppose les valeurs de tab dans 0..9
int[] freq= new int[10]; // valeurs de 0 à 9 Fréquences des valeurs de tab
freq={1,0,3,1,0,2,0,1,0,2} Fréquences cumulées des valeurs de tab
freq={1,1,4,5,5,7,7,8,8,10}
Connaissant freq on peut trier tab en un seul passage !
Tri par comptage de fréquence
void triComptage(int[] t, int k){ // t prend ses valeurs dans 0..k int[] f=new int[k+1]; int n=t.length ; int[] r=new int[n]; for(int i=0 ; i<k+1 ; i++) f[i]=0 ;
// remplir f avec les frequences for(int j=0 ; j<n ; j++) f[t[j]]++ ;
// remplir f avec les frequences cumulees for(int i=1 ; i<k+1 ; i++) f[i]=f[i] + f[i-1] ; // trier t dans r
…}
Tri par comptage de fréquence
void triComptage(int[] t, int k){ // t prend ses valeurs dans 0..k int[] f=new int[k+1]; int n=t.length ; int[] r=new int[n];
… // trier t dans r
for (int j=n-1 ; j>=0 ; j--) { r[f[t[j]]-1]=t[j]; f[t[j]]-- ; } // recopier r dans t for(int j=0 ; j<n ; j++) t[j]=r[j] ;}
Tri rapide (quicksort) Hoare (1962)
1. Choisir un élément pivot PT[0].. T[k-1] T[k] T[K+1]..T[N]
2. Partitionner le tableau autour du pivot P– Tous les éléments avant P sont plus petits que P
T[0]..T[k-1] <= P
– Tous les éléments après P sont plus grands que PP < T[K+1]..T[N]
3. Recommencer– sur T[0]..T[k]
– sur T[K+1]..T[N]
Tri rapide (quicksort) Hoare (1962)
1. Choisir un élément pivot
{4,3,9,1,0,5,6,3,8,2};
2. Partitionner le tableau autour du pivot P
{2,3,3,1,0,4,6,5,8,9}
Tous les éléments avant P sont plus petits que P
Tous les éléments après P sont plus grands que P
Partitionner autour du pivot
int partition(int[] t, int g, int d){ int pivot = t[g], i = g, j = d ; while (i<j) { while(i<=d && t[i]<=pivot) i++; while(t[j]>pivot) j--; if (i<j) echanger(t,i,j); } // j est la position finale du pivot t[g] = t[j]; t[j] = pivot; return j;}
Tri rapide
La fonction partition() est itérative et sa complexité est en O(n)
Testé sur trois tableaux de taille 1000 dont les éléments sont respectivement, générés aléatoirement, générés par ordre croissant, générés par ordre décroissant, le nombre d'échanges d'éléments observé est respectivement : 5477, 999, et 250 999
On montre que la complexité moyenne du tri rapide est en O(n.log(n))
Mais comme il n'y aucune raison pour que la partition décompose une table de longueur n en deux tables de longueur n/2, la complexité dans le pire des cas est très mauvaise, puisqu'elle est en n2
Cela existe déjà en Java !
void main(String[] args){
int[] tab={4,3,9,1,0,5,6,3,8,2,0};
afficherTableau(tab);
// triRapide(tab,0,tab.length-1);
java.util.Arrays.sort(tab);
afficherTableau(tab);
}