Projet Caml : Codage de Huffman

publicité
Projet Caml :
Codage de Huffman
Didier Siphaxay
1A INFO N7 : Groupe F
11 décembre 2007
Résumé
Ce projet est basé sur l’algorithme de compression / décompression crée par M. Huffman en 1952
.La compression de données fait partie du quotidien de millions d’utilisateurs d’ordinateurs à travers
le monde. Avec la multiplication des sources d’informations il est préférable de mieux gérer l’espace
mémoire qui est souvent assez limité sur nos machines même si de nos jours les capacités sont de plus
en plus grandes pour une taille de plus en plus petite.
1
Siphaxay Didier
Programmation fonctionnelle CAML
Projet P1
Table des matières
1 Présentation générale
1.1 Introduction . . . . . . .
1.2 Distribution fournie . .
1.3 Les spécifications exigées
1.4 Choix de spécifications .
. . .
. . .
sujet
. . .
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
3
3
3
4
5
2 Conception
2.1 Structure globale de l’application
2.2 Burrows Wheeler . . . . . . . . .
2.3 Move to Front . . . . . . . . . .
2.4 Huffman . . . . . . . . . . . . . .
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
6
6
7
8
9
3 Codage
3.1 Burrows Wheeler . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
3.2 Move to Front . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
3.3 Huffman . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
10
10
12
13
4 Tests
4.1 Méthodes de test . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
14
15
5 Conclusion
5.1 Travail effectué . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
5.2 Le langage Caml . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
5.3 Avis personel . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
16
16
16
16
6 Annexes
6.1 Listing des fichiers de test (globaux) . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
6.2 Statistiques sur les tests usines . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
17
17
17
. .
. .
du
. .
2
Siphaxay Didier
Programmation fonctionnelle CAML
1
1.1
Projet P1
Présentation générale
Introduction
L’objectif de ce projet est la réalisation d’un algorithme de compression / décompression basé sur
les arbres de Huffman. Pour améliorer l’efficacité de cette technique, deux autres méthodes lui seront
adjointes : la méthode de Burrows-wheeler ainsi que celle du ”Move-to-Front”.
Pour compresser des données, on utilise une compression par arbre (méthode d’Huffman). Celle ci
peut se décomposer ainsi : on va d’abord séparer la donnée source en unités de même taille (e.g. des
caractères) et associer chacune de ces unités à sa multiplicité dans dans la donnée source. Cette méthode
est ainsi semi-adaptative, en effet on analyse une première fois le document afin d’extraire des statistiques
sur les unités que l’on range dans l’arbre d’huffman pour pouvoir mieux optimiser la compression. Il existe
bien d’autres méthodes qui sont basés soit sur la fréquence d’apparatition de l’unité dans une langue soit
d’autres paramètres.
Pour retrouver l’ordonnancement de ces unités, on va se servir d’une suite de bits qui nous permettra
de naviguer dans l’arbre ainsi composé. Le nombre de bit attribué à une unité dépendra de sa multiplicité
dans la donnée source, ainsi les unités les plus fréquentes seront codés sur un code binaire très court alors
que les données relativement peu présentes le seront par des codes plus long.
Pour améliorer l’efficacité de cette méthode. on va adjoindre une méthode de codage différentiel
qui va évaluer les distances entre les unités et leurs précédentes dans la données sources. Cette méthode
ne compresse pas les données mais permet d’obtenir une compression plus élévés grâce au codage de
Huffman dans le cas où les occurences d’une même unité sont proches. En effet dans ce cas les distantes
sont relativement petites et on obtient donc une sensible augmentation de donnée redondantes (gage de
bonne compression pour la compression par arbre). C’est l’objet de la méthode ”Move-to-front”.
Enfin pour favoriser la méthode précedente, la méthode de ”Burrows-wheeler” entre en jeu. Cette
méthode n’est rien d’autre qu’une méthode permettant de rapprocher les unités identiques entre elles afin
de réduire leurs distances relatives dans la donnée source. Elle n’engendre elle non plus aucun gain de
compression mais elle est facilement reversible. Cette méthode est cependant assez lourde et ne peut être
appliqué à des données trop grandes.
1.2
Distribution fournie
Voici le listing des fournitures de l’archive qui a été mise à notre disposition pour ce projet.
– main.cmo
– defaut.mli
– defaut.cmi
– defaut.cmo
– huffman.mli
– movetofront.mli
– burrows wheeler.mli
– Makefile
– ...
Les différents fichiers ”mli” sont des fichiers de spécification que l’on respecte au travers des types à
adopter pour le codage. Ces dernières assurent l’interface entre le programme principal et les différents
modules mis en place.
L’item ”main.cmo” est le fichier objet issue de la compilation du projet. Cet objet est donc la pour
assurer la dernière étape de la création de l’éxécutable ”huffman” par le procédé d’édition des liens grâce
à la commande ”make”. Les phases d’entrées-sorties ainsi que l’interaction avec l’utilisateur qui permet
d’appeler les différents modules en mode encodage ou décodage sont implantées dans ce fichier.
Les items ”defaut.cmo”, ”defaut.cmi”, ”defaut.mli” sont des fichiers qui permettent de remplacer
les fonctions que l’on doit coder dans le cas d’un malfonctionnement de ces derniers. Ces fichiers ne
permettent aucune compression des données et servent donc seulement de support pour la compilation.
3
Siphaxay Didier
Programmation fonctionnelle CAML
Projet P1
Enfin le fichier ”Makefile” est un fichier contenant des commandes shell qui permettent de compiler et
faire l’édition des liens de l’ensemble du programme. On peut aisément les programmer pour n’importe
quel type de langage. Leur utilisation passe par la commande ”make”.
Les bases étant posées on doit se questionner sur les contraintes du projet : on en a déjà un exemple
avec les différents fichiers mis à notre disposition.
1.3
Les spécifications exigées du sujet
Le projet doit être écrit en Caml, langage de programmation imposé, et ceci dans un style programmation fonctionnelle i.e. sans tableaux, ni réferences, ni procédures.
Pour ce projet, il nous a été fournie une distribution qui restreint déjà notre travail. En effet les
fichiers ”mli” cités précédemment sont là pour poser un contrat entre le programmeur et le spécificateur
en énonçant les profils des fonctions attendues.
Voici toutes les fonctionnalités développées dans le cadre de ce projet que l’on peut retrouver dans les
fichiers ’mli’.
– Burrows wheeler : encode
Encode en utilisant la transformée de Burrows-Wheeler.
Renvoie l’indice de position ainsi que la séquence des derniers caractères
– Burrows wheeler : decode
Décode la séquence à partir de l’indice de position et la séquence des derniers caractères.
– Move to front : encode
Encode une liste d’unités grâce à une fonction de codage arbitraire par défaut. Pour les caractères,
cela peut être le code ASCII par exemple
– Move to front : decode
Décode la liste d’unités grâce à une fonction de décodage par défaut. Cette fonction doit être la
réciproque de celle utilisée par la fonction ’encode’
– Huffman : type ’a huffman (à definir)
Type de l’arbre binaire utilisé pour coder le document
– Huffman : build-tree
Prend une liste d’éléments et construit l’arbre de codage
– Huffman : encode
Prend une liste d’éléments et renvoie le couple (arbre de codage de huffman, codage binaire de la
liste)
– Huffman : decode
Prend un arbre de codage de huffman et une liste de booléens et reconstruit la liste d’éléments
décodée
Un jeu de test sera élaboré pour un ”test site” permettant de vérifier la fonctionnalité du programme
présenté. Ce jeu de test devra être le plus fin possible rassemblant l’intégralité des cas possibles ou du
moins dans les limites posées par le programme principal codé dans le fichier ”main.cmo”.
4
Siphaxay Didier
Programmation fonctionnelle CAML
1.4
Projet P1
Choix de spécifications
Contraintes personnelles : même si le client ne l’a expressement pas éxiger, pour un souci de rapidité, je
préfererai concevoir les différentes fonctionnalités dans un vision d’optimisation temporelle puisque c’est
une priorité qui sera imposé dans le travail d’ingénieur. Le deuxième effet étant de ne pas faire patienter
l’utilisateur du programme trop longtemps avant d’avoir un résultat.
Le choix majeur dans ce projet fut l’élaboration d’un type ’a huffman de façon à optimiser la
taille de la donnée compressé. Pour cela on a du passer d’abord par un type qui permettait d’équilibrer
l’arbre de codage pour permettre d’obtenir des codes binaires d’une longueur de ’log n’ pour un élément
d’une liste de longueur n.
Listing 1: Type ’a arbre : type intermédiaire
type ’a arbre =
| Feuille of int ∗’a
|Noeud of (’a arbre∗int∗’a arbre );;
Listing 2: Type ’a huffman
type ’a huffman =
|Vide
|Leaf of ’ a
|Node of ’a huffman∗’a huffman;;
La liste booléene associé à cet arbre est un peu à part mais fait partie entière de la donnée compressée.
Les fonctionnalitées auxquelles aura accès l’utilisateur sont au nombre de, en tout et pour tout,
2 ; celle pour compresser les données et celle pour décompresser des fichiers. Ceci à travers les commandes
suivantes :
– huffman -e <<fichier>> : produit le fichier codé <<fichier>>.encoded à partir du fichier source
<<fichier>>
– huffman -d <<fichier>> : produit le fichier décodé <<fichier>>.decoded à partir du fichier codé
<<fichier>>.encoded
5
Siphaxay Didier
Programmation fonctionnelle CAML
2
2.1
Projet P1
Conception
Structure globale de l’application
La conception de cette application réside dans l’agencement des 3 méthodes servant à la compression
de Huffman. ainsi :
Fig. 1 – Schéma général
Dans la présentation générale, on a fait mention de l’ajout de 2 méthodes qui s’imbrique dans le
processus : ici on le voit clairement. La transformée de Burrows-Wheeler va fournir une liste dont les
éléments identiques sont rapprochés entre eux. Puis le module ”Move-to-Front” prend la relève en codant
les distances relatives pour que le codage de Huffman soit efficace.
Les modules sont donc répatis de la manière suivante :
– Burrows wheeler
– Move to front
– Huffman
Chacun de ces modules contiennt de paire une fonction d’encodage et de décodage dont les algorithmes
sont énoncés dans la section ’Codage’. Ici l’intégration des modules dans l’application principale n’est pas
de notre ressort puisque déjà fournie par le biais de l’archive. Ici nous allons donc simplement énoncer les
types retenus qui réaliseront le profil des fonctions à coder ainsi qu’un descriptif des différentes méthodes.
6
Siphaxay Didier
Programmation fonctionnelle CAML
2.2
Projet P1
Burrows Wheeler
Fonction encode :
– définition :
| Fonction : encode : ’a list -> (int*’a list) :
| Resultat : Renvoie l’indice de position ainsi que la séquence des derniers caractères.
Usage :
On va suivre un exemple d’encodage pour déterminer l’algorithme à retenir afin de le coder. Dans un
premier temps on crée la liste des permutations circulaires de la liste à coder :
s1
T
E
T
X
E
E
T
E
T
X
X
E
T
E
T
T
X
E
T
E
s5
E
T
X
E
T
Après un tri, on observe dans la colone s5 un rapprochement des caractères, ceci est le fondement de
cette méthode. On va donc récupérer cette dernière colonne et repérer la ligne où apparait la liste à coder.
position
1
2
3
4
5
s1
E
E
T
T
X
T
X
E
E
T
E
T
T
X
E
X
E
E
T
T
s5
T
T
X
E
E
Ainsi le résultat va être (indice de la position : 4, et liste codée [’T’;’T’;’X’;’E’;’E’])
Fonction decode :
– définition :
| Fonction : decode : (int*’a list) -> ’a list:
| Resultat : Décode la séquence à partir de l’indice de position et la séquence des derniers caractères.
Usage : A partir de la liste codée (et celle qui est classée) et la première position. On obtient ce tableau
suivant :
1 2 3 4 5
Codé
T T X E E
Classé E E T T X
Pour reconstituer ce mot il faut partir dans la liste classée à la 4ème position c.a.d. le 2ème T puis
on cherche le 2ème T dans la liste codée pour trouver ensuite le 2ème E et on continue en cherchant de
2ème E dans la liste codée et ainsi de suite . . .
On obtient donc par reconstitution : le mot ”TEXTE”
7
Siphaxay Didier
Programmation fonctionnelle CAML
2.3
Projet P1
Move to Front
Fonction encode :
– définition :
| Fonction : encode : (’a -> int) -> ’a list -> ’a :
| Resultat : Encode une liste d’unités grâce à une fonction de codage arbitraire par défaut. Pour les
caractères, cela peut être le code ASCII par exemple
Usage :
Pour simplifier, on utilise des caracteres en ne considérant que les lettres de l’alphabet mais cette démarche
s’applique sur tout type d’éléments à coder grâce à une fonction qui permet de coder les éléments en entiers
(e.g. Char.code). Le tableau est tout d’abord initialisé en rangeant les caractères utilisés pour le codage
comme ceci :
Indice
0 1 2 3 4 5 6 . . . 25
Caractère A B C D E F G . . .
Z
Lorsqu’un caractère est lu, son indice est émis, puis ce caractère est placé en première position et tous
les autres caractères décalés (d’où le nom de Move to Front). Par exemple si le premier caractère à coder
est un E, le tableau deviendrait :
Indice
0 1 2 3 4 5 6 . . . 25
Caractère E A B C D E F . . .
Z
Ainsi, lorsque des caractères semblables se suivent (cas de la transformée de Burrows-Wheeler), le
flux émis contiendra beaucoup de 0, ce qui dans une compression statistique (type codage de Huffman)
augmentera considérablement le gain de compression. On note que dans ce cas, l’émission d’un 0 laisse le
tableau identique, et que dans les autre cas, le réarrangement ne concerne que les premiers éléments du
tableau.
Par exemple, la séquence EEEEEA serait transformée en la suite 400001 ; le tableau évoluerait comme
suit :
Indice
0 1 2
3
4 5 6 . . . 25
État initial
A B C D
E F G ...
Z
Tableau modifié par le premier E
E A B
C
D F G ...
Z
Tableau conservé 4 par les 4 E suivants
...
Tableau modifié par le A
A E B
C
D F G ...
Z
Ce déplacement peut être gérer de manière très simple dans un accumulateur (mis en oeuvre au niveau du codage). Mémoriser les déplacements devient aisé et réduit la complexité de la fonction puisque
j’ai pensé à un algorithme plus intuitif qui modifie la fonction f à chaque nouvel appel de la fonction
d’encodage mais le nombre d’instruction devient assez vite ingérable.
Fonction decode :
– définition :
| Fonction : decode : (’a -> int) -> ’a list -> ’a :
| Resultat : Décode la liste d’unités grâce à une fonction de décodage par défaut. Cette fonction doit
être la réciproque de celle utilisée par la fonction ’encode’
Il s’agit de la réciproque de la méthode utilisée, avec un système identique de déplacement en tête
mais avec une fonction qui récupère un élément à partir d’un nombre.
8
Siphaxay Didier
Programmation fonctionnelle CAML
2.4
Projet P1
Huffman
Fonction build tree :
– définition :
| Fonction : build tree : ’a list -> ’a huffmantree :
| Resultat : Prend une liste d’éléments et construit l’arbre de codage.
Fig. 2 – Exemple d’arbre d’huffman
Afin de créer cet arbre il faut dans un premier temps faire une première analyse de l’ensemble que
l’on veut coder pour déterminer les multiplicités des différents éléments.
Ensuite Créer Chaque ”Feuille” de cet arbre puis les fusionner en prenant compte du ”poids” (somme
des occurences de tous ses fils) de chaque branche. Ainsi on équilibre l’arbre en fonction du nombre d’apparition de l’élément dans la liste à coder.
Fonction encode :
– définition :
| Fonction : encode : ’a list -> ’a huffmantree * bool list :
| Resultat : rend une liste d’éléments et renvoie le couple (arbre de codage de huffman, codage binaire de la liste)
Pour cette fonction d’encodage on peut arbitrairement posé une condition pour l’élaboration des
chemins vers les éléments de l’ensemble. En effet, on peut considérer que la branche gauche d’un noeud
peut représenter le booléen false (0) et ainsi la branche droite vaudrait true (1).
Fig. 3 – Exemple d’arbre d’huffman + chemins
On prend donc chaque élément à coder et on extrait la liste de booléens qui mène à ce dernier.
9
Siphaxay Didier
Programmation fonctionnelle CAML
Projet P1
Fonction decode :
– définition :
| Fonction : decode : ’a huffmantree * bool list -> ’a list :
| Resultat : Prend un arbre de codage de huffman et une liste de booléens et reconstruit la liste
d’éléments décodée
Grâce à la liste de booléens, on parcourt l’arbre d’huffman selon donc l’ordre arbitraire que l’on s’est
fixé auparavant. On procède booléen par booléen pour avancer dans l’arbre et dès que l’on tombe sur une
feuille alors on renvoie cet élément. Par contre si cette liste ne se termine pas à une feuille alors elle était
mal construite (exception à gérer). Ainsi on reconstitue la liste d’origine.
3
Codage
Dans cette section, j’ai décidé de ne mettre que les algorithmes généraux des différentes fonctions
implémentées dans chaque module. Pour raffiner ou mieux comprendre, je vous conseille de regarder les
commentaires des fonctions numérotées dans le code source.
(n) : Chaque item numéroté représente une sous-fonction auxiliaire (récursive non-élémentaire) implémentée
dans le cadre de la fonction générale. Leur profil est disponible et permet de mieux comprendre le typage
adopté dans cette solution (cf. Annexe).
3.1
Burrows Wheeler
Fonction encode :
– raffinage :
(1) Créer la liste des permutations circulaires à partir de la liste d’origine
(2) Trier cette liste par ordre croissant
(3) Chercher la position du mot d’origine dans cette liste de liste
(4) Récupérer les derniers éléments de chaque liste dans cette liste de liste pour former
la liste codée
(1)
(2)
(3)
(4)
encode aux : ’a list -> ’a list -> ’a list list
trif : (’a -> ’a -> bool) -> ’a list -> ’a list
n fin : ’a list list -> ’a list -> int
inclus dans la fonction encode
Choix des types des fonctions auxiliaires : Il nous fallait un type pour représenter l’ensemble des permutation circulaires de la liste à coder. Le plus judicieux fut compte tenu des contraintes de prendre
des listes de liste d’éléments qui représente en somme une sorte de matrice muni de permutation et dans
laquelle la dernière colone représente le mot codé. On récupère par ailleurs l’entier désignant la position
de la liste d’origine après le tri des lignes de la matrice, dans la fonction n fin.
Fonction decode :
– raffinage :
(1) Créer la liste classée à partir de la liste à décoder
(2) Créer la liste des paires (élément, n-ieme occurence)
avec leur indice absolu
(3) Rechercher le premier élément à partir de l’indice de
Mettre cet élément dans une liste auxiliaire
(4) Tantque la liste n’est pas vide faire
(5) Rechercher récursivement chaque élément suivant de
Mettre cet élément dans la liste auxiliaire
Passer à l’élément suivant
Fin tanque
Récupérer la liste décodée à partir de la liste auxilaire
10
dans les 2 listes précédentes
départ
la liste
ré-ordonnée
Siphaxay Didier
Programmation fonctionnelle CAML
(1)
(2)
(3)
(4)
(5)
Projet P1
occurence : ’a -> int -> ’a list -> int
creer paires : ’a list -> ’a list -> ((’a*int)*(’a*int)*int)
recherche : int -> ’a list -> (’a*int)
re ordonner : ’a list -> int -> (’a*int) list
suivant : (’a*int) -> ’a list -> (’a*int)
Choix des types des fonctions auxiliaires : Pour effectuer les recherches on doit créer un type qui
permet de connaı̂tre la n-ieme occurence de l’élémnent sur lequel on est positionné. Donc c’est le type
proposé dans l’algorithme qui se code ainsi : ((’a*int)*(’a*int)*int). Pour que la fonction suivant puisse
passer récursivement d’un élément à un autre j’ai privilégié un le passage de la paire pour ne faire qu’une
seule comparaison logique (bien que la machine fait la comparaison terme à terme). Pour illustrer cette
naviguation dans la liste des paires voici le code proposé pour retrouvé l’ordre de la liste d’origine.
Listing 3: Exemple du code de la recomposition de la liste d’origine
(∗−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−
| Fonction : re ordonner : ’ a list −$>$ int −$>$ (’a∗int) list :
| Parametres :
|
l : ’ a list : liste creer a partir de la fonction creer paires
|
pabs : int : position absolue
| Resultat : Renvoie liste des paires qui se suivent pour former la liste decode
| Semantique :
| Dans la liste des paires on recherche la premiere paire designe par
| sa position absolue puis recursivement les suivants jusqu’a reformer la liste
| prealablement encodee encore muni des postions relatives.
| Complexité : Ø(n2)
−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−∗)
let re ordonner l pabs =
let lg = List.length l
in
let rec ord aux n pattern =
match n with
| n when n=lg −$>$ [(suivant pattern l)]
| n when n=1 −$>$ let g = recherche pabs l in
g :: ord aux (n+1) g
| n −$>$
let g = suivant pattern l in
g :: ord aux (n+1) g
in
ord aux 1 (recherche pabs l );;
11
Siphaxay Didier
Programmation fonctionnelle CAML
3.2
Projet P1
Move to Front
Fonction encode :
- raffinage :
Tantque la liste n’est pas vide faire
si l’élément en cours n’est pas dans la liste auxiliaire alors
(1) Appliquer la fonction f à cet élément auquel il faut ajouter le nombre ...
... d’éléments qui lui sont supérieurs dans la liste auxilaire
Mettre en t^
ete cet élément dans la liste auxilaire
sinon
(2) Récupérer la position de l’élément dans la liste auxiliaire
(3) Déplacer cet élément en t^
ete de liste auxiliaire
fin si
Passer à l’élément suivant
fin tantque
(1) nb elem sup a : ’a -> ’a list -> int
(2) position : ’a -> ’a list -> int
(3) movetofirst : ’a -> ’a list -> ’a list -> ’a list
Je ne réalise que des opérations quasi-élémentaires puisque j’ai simplifier la mise en oeuvre déployée
dans la conception (première méthode) avec l’apparition d’un accumulateur gérant les déplacement en
tête de liste.
Fonction decode :
- raffinage :
Tantque la liste n’est pas vide faire
Appliquer la fonction f(modifié ou pas) sur cet élément
(1) Modifier la fonction f de façon à retenir toute les modifications précédentes
Passer à l’élément suivant
fin tantque
(1) mtf int : (int -> ’a) -> int -> int -> ’a
Pour bien enregistrer les modifications sur la fonction f grâce à la fonction mtf int dont le code est
dévoilé ici, il faut passer non pas la fonction mtf int mais sa version partielle avec son premier argument.
Ce dernier est celui qui retiendra toutes les modifications effectuées sur la liste.
12
Siphaxay Didier
Programmation fonctionnelle CAML
Projet P1
Listing 4: Exemple du code de la modification de la fonction f
(∗−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−
| Fonction : mtf int : ( int −$>$ ’a) −$>$ int −$>$ int −$>$ ’a :
| Parametres :
|
f : int −$>$ ’a: fonction de codage (exemple Char.chr)
|
a : int : entier de la liste
|
n : int : entier suivant de la liste
| Resultat : le resultat de la fonction f en fonction de a et n
| Semantique :
| si les 2 entiers qui se suivent sont identiques alors leur distance
| est egale a 0 cela veut dire qu’ il faut recupere le code de l ’element a
| sinon cela veut dire que que c’est l ’element code par n mais si l ’element
| precedent ’a ’ est superieur à ’ n’ alors ca veut dire que que cet element
| ’ a ’ a ete deplace en tete et que le code de ’n’ est donc modifie de 1 unite.
| Complexité : Ø(constant), 2 operations de comparaisons
−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−∗)
let mtf int f a n =
if n=0 then f a
else if a < n then f n
else f (n−1);;
3.3
Huffman
Fonction build tree :
- raffinage
(1) Créer le multi-ensemble à partir de la liste de départ
(2) Transformer ce multi-ensemble en Feuille
(3) Trier ce multi-ensemble par occurence croissante
Tant qu’il ne reste plus qu’un élément faire
(4) Fusionner les 2 premiers éléments
(5) Ré-insérer le resultat de la fusion dans la liste en conservant l’ordre
Passer à l’élément suivant
Fin tantque
(6) Enlever les occurences devenues inutiles de cet arbre. cette étape définit le type ’a
huffman
(1)
(2)
(3)
(4)
(5)
(6)
occ : ’a list -> (int*’a) list
transfo : ’a list -> ’a arbre list (dans la fonction build tree)
trif : (’a -> ’a -> bool) -> ’a list -> ’a list (tri fusion)
nouveau noeud : ’a arbre -> ’a arbre -> ’a arbre (dans la focntion build tree associé à ’fusionne’)
insert : ’a arbre -> ’a arbre list -> ’a arbre list
create huffman tree : ’a arbre ->’a huffman (dans la focntion build tree)
Choix du typage : pour des raisons d’équilibrage je suis passé par un type intermédiaire le type ’a
arbre qui possède les occurences à chaque de tous ses fils. Je travaille essentiellement sur ce type dans
tout le code afin de bien gérer la construction de l’arbre d’huffman. Vous pourrez avoir plus de détails sur
la contruction de cet arbre en regardant les commentaires du code source mais cet algorithme se suffit à
lui-même.
13
Siphaxay Didier
Programmation fonctionnelle CAML
Projet P1
Fonction encode :
- raffinage
(*) Créer l’arbre de codage à partir du raffinage précédent
(1) Créer la table des chemins de tous les éléments de l’arbre de codage
Tant que la liste à coder n’est pas vide faire
(2) Récupérer le chemin associé à l’élément présent dans l’arbre depuis la table des
chemins
Passer à l’élément suivant
Fin tantque
(*) réaliser par la fonction build tree.
(1) tab chemins : ’a huffman -> ’a list -> (’a * bool list) list
(2) research : ’a -> (’a * bool list) list -> bool list(associé à create bool list)
Choix du typage : Pour simplifier les recherches, on met en oeuvre une pré-recherche (tab chemins)
dans l’arbre d’huffman pour minimiser les appels à une fonction de recherche dans l’arbre. puis on récupère
la liste de boléen ainsi former au passage de l’élément en cours de la liste à coder.
Fonction decode :
- raffinage
Tantque la liste de booléens n’est pas vide faire
Avancer d’un pas dans l’arbre en fonction du bit en cours
Si cet élément est une Feuille alors
Récupérer l’élément de cette Feuille
Recommencer à partir du haut de l’arbre
Sinon
Continuer à avancer dans l’arbre
Fin si
Passer au booléen suivant
Fin tantque
Cet algorithme ne fait que parcourir sans opérations non-élémentaires l’arbre d’huffman grâce à la
liste de booléen. On navigue dans cet arbre pas à pas afin de trouver la feuille conrrespondante à l’élément
à décoder.
4
Tests
Les tests sont d’une importance capitales autant pour le programmeur, le concepteur, le spécificateur
et le client.
C’est pour cela qu’il existe plusieurs types de tests :
– Les tests unitaires (programmation) qui doivent respecter les profils des fonctions demandés
– Les tests d’intégration (agencement des modules) qui permettent de tester si les différents modules
arrivent à communiquer entre eux
– Les tests usines qui rassemblent les test globaux de l’application livrée
– La recette : test évaluant le résultat à ce qu’attendait le client
– Les tests site qui sont là pour finaliser le contrat en vérifiant que l’application installée fonctionne
à l’identique par rapport au test usines
Les test unitaires ont été codé dans le code sous sous forme de commentaires. J’ai ajouté quelques
tests d’intégration au sein de chaque module. Le test global se fera avec une série de fichier que j’aurai
préparé et dont je donnerai le listing en annexes (cf. Annexe 1).
La recette et le test site (réalisé le 12/12/2007) se feront donc avec les fichiers composants le listing
précédent.
14
Siphaxay Didier
Programmation fonctionnelle CAML
4.1
Pour
–
–
–
Projet P1
Méthodes de test
valider un test quelque soit le niveau, il faut respecter différents paramètres, ici :
le type de donnée en entrée (généricité)
les propriétés des données
les capacités physiques de la machines, . . .
Organisation des tests :
Il faut vérifié en général plusieurs type de cas :
– Cas aux bornes (limites) : qui concerne souvent les objets vide ou muni d’un seul élément ou au
borne de l’ensemble considéré
– Cas d’exception : qui concerne tous les cas que vous auriez pu éventuellement jugé comme exceptionnel (cas impossible en théorie à gérer)
– Cas généraux : des tests génériques (les types aussi dans ce cas précis) testant le bon fonctionnement
de la fonction ou de l’application
Test unitaires
Burrows Wheeler
– Cas aux bornes (limites) : les listes vides et les ensemble muni d’un seul élément ou trop grand
– Cas d’exception : (decode seulement) un indice hors des bornes (0..lenght l)
– Cas généraux : le test de base avec tous les types de base possibles (bool, int, float, string, char, ...)
Move-to-Front
– Cas aux bornes (limites) : les listes vides et les ensemble muni d’un seul élément ou trop grand
– Cas d’exception : aucun
– Cas généraux : le test de base avec tous les types de base possibles (bool, int, float, string, char, ...)
Huffman
– Cas aux bornes (limites) : les listes vides et les ensemble muni d’un seul élément ou trop grand
– Cas d’exception : (decode seulement) une liste booléene mal contruite
– Cas généraux : le test de base avec tous les types de base possibles (bool, int, float, string, char, ...)
Test d’intégration dans chaque module
– Cas aux bornes (limites) : les listes vides et les ensemble muni d’un seul élément ou trop grand
– Cas d’exception : aucun
– Cas généraux : le test de base avec tous les types de base possibles (bool, int, float, string, char, ...)
Test globaux
– Cas aux bornes (limites) : fichiers vides ou avec un seul élément ou trop grand
– Cas d’exception : un fichier contenant un type non valide
– Cas généraux : le test de base avec des fichiers de plus ou moins grandes tailles (test de performance!)
Les tests usines (sur les machines de l’ENSEEIHT) ont été effectué et les résultats sont dans les annexes. On remarque bien que c’est la fonction encode de burrows-wheeler qui prend le plus de temps
puisque je vous l’ai déjà expliqué, il faut générer la matrice des permutations. Cette étape prends de plus
en plus de temps dès lors que l’on augmente la taille de la liste passée en paramètre. Une version itérative
de cette fonction réduirai le temps d’éxécution.
Vous le remarquerez j’ai aussi testé des cas où les fichiers d’entrées sont incorrects de part leur type : ce
qui provoque une erreur dans le programme. De plus je peux aussi encodé des fichiers vides et des fichiers
”random” avec la commande dd sur le /dev/zero et le /dev/urandom qui permet de générer des fichiers
remplis de zéro binaire respectivement des données aléatoires, très utile pour tester nos fonctions.
15
Siphaxay Didier
Programmation fonctionnelle CAML
5
5.1
Projet P1
Conclusion
Travail effectué
En conclusion après une série de tests réussis, mon programme fonctionne du mieux que je pouvais
l’espérer. je dirai que ce projet fut assez simple à réaliser compte tenu de l’abondance d’informations que
l’on peut trouver sur l’internet.
Du point de vue de mon travail, je trouve que les algorithmes que j’ai utilisé sont d’une complexité
assez basse compte tenu des limitations du sujet qui imposait l’utilisation d’un style fonctionnel.
Ainsi à travers ces algorithmes, on ne peut pas traiter des quantités de données trop importantes surtout en voyant ce que réalise la fonction encode de Burrows-Wheeler, en effet elle crée ce que je pourrai
nommer une matrice de permutation de la liste qui contient n2 éléments. Cette étape peut mettre être
interrompu car la mémoire physique de l’ordinateur ne le permet pas. (stack overflow)
La recursivité impose alors le parcours intégrale de cette matrice alors que les références sont astucieuses dans ce cas : cela pourrai éviter des millions d’instructions à la machine et accélérer le processus.
Néanmoins on se rend compte que si on a un nombre réduit d’élément dans cette matrice : les durées
d’éxécution sont minimes. Une amélioration qui concerne le programme principal pourrai être de sollicité
un ”découpeur” de fichier avec une certaine taille (à déterminer) pour ensuite lancer x processus qui
chacun effectuerai l’encodage. Malheureusement, même si un gain de temps d’éxécution est prévisible, la
compression ne serait plus aussi efficace car on définit à chaque fois un arbre qui contiendrait souvent un
élément contenu dans un autre ... d’où encore un problème de gestion des doublons et de la taille de la
donnée compressé.
Donc en somme, réaliser le module gérant la transformé de Burrows-Wheeler dans un style itératif
avec des tableaux et des références se l’alternative idéale.
5.2
Le langage Caml
Concernant le langage Caml, beaucoup diront que c’est un langage dans lequel on ne se prend pas
trop la tête avec la gestion dynamique des objets que l’on exploite, et je ne les contredirai pas en effet,
il est plaisant de pouvoir programmer assez rapidement des algorithmes sans penser à des contraintes
techniques assez pointues, ce qui est généralement le cas lorsque l’on programme en C.
Cependant ce n’est pas un langage qui est adapté à une utilisation visant à augmenter les performances
de votre programme, ce qui devient une question préoccupante dans l’industrie. Il peut par contre être
un très bon langage pour des prototypes de logiciel.
5.3
Avis personel
C’est la première fois que je réalise un travail de ce type. Il est vrai que j’ai déjà une expérience
de la programmation mais je n’ai jamais vraiment réalisé quel algorithme était lié à certain type de
compression. Ici je pense que c’est un plus pour moi puisque j’ai commencé à aborder le sujet, et que je
vais peut-être approfondir mes recherches dessus dans le cas où j’aurai besoin de faire de la compression
de donnée dans ma vie active.
Ce projet fut intéressant à réaliser, même si les conditions imposées ne m’ont pas vraiment ravi. Il est
vraiment sujet à une ouverture sur le domaine de l’optimisation de la gestion d’espace disque et cela est
un plus pour notre vision en tant qu’informaticien.
16
Siphaxay Didier
Programmation fonctionnelle CAML
6
Projet P1
Annexes
6.1
Listing des fichiers de test (globaux)
Listing 5: Liste des fichiers à utiliser lors des test globaux
<<Dans le dossier FichierTest>>
vide. txt
−− fichier vide
zero. txt
−− fichier de 5Ko remplie de zero binaire ( crée à partir de la commande dd)
random.txt
−− fichier de 5Ko remplie aléatoirement (crée à partir de la commande dd)
France.txt
−− texte de 2917 caractères
nutellaraton . txt
−− texte de 6666 caractères
−− fichier de 100Ko remplie aléatoirement (crée à partir de la commande dd)
trop grand.txt
6.2
Statistiques sur les tests usines
Encodage :
./huffman -e ../FichierTest/nutellaraton.txt
Burrows-Wheeler transformation ..... 58.6840999126sec
Move to Front encoding ............. 5.66538000107sec
Huffman encoding ................... 0.409476995468sec
Compression rate achieved .......... 52/100
./huffman -e ../FichierTest/vide.txt
Burrows-Wheeler transformation ..... 2.86102294922e-06sec
Move to Front encoding ............. 0.000128984451294sec
Huffman encoding ................... 1.21593475342e-05sec
Compression rate achieved .......... -1/100
./huffman -e ../FichierTest/zero.txt
Burrows-Wheeler transformation ..... 52.8612508774sec
Move to Front encoding ............. 0.0151159763336sec
Huffman encoding ................... 0.0109641551971sec
Compression rate achieved .......... 13/100
./huffman -e ../FichierTest/random.txt
Burrows-Wheeler transformation ..... 29.701595068sec
Move to Front encoding ............. 45.3592300415sec
Huffman encoding ................... 0.836948871613sec
Compression rate achieved .......... 127/100
./huffman -e ../FichierTest/France.txt
Burrows-Wheeler transformation ..... 10.0341439247sec
Move to Front encoding ............. 1.84397697449sec
Huffman encoding ................... 0.193593978882sec
Compression rate achieved .......... 67/100
./huffman -e ../FichierTest/nutellaraton.txt
Burrows-Wheeler transformation ..... 52.9077839851sec
Move to Front encoding ............. 5.11091113091sec
Huffman encoding ................... 0.408653020859sec
Compression rate achieved .......... 52/100
./huffman -e ../FichierTest/trop grand.txt
Fatal error: exception Stack overflow
17
Siphaxay Didier
Programmation fonctionnelle CAML
Projet P1
Décodage
./huffman -d ../FichierTest/vide.txt.encoded
Huffman decoding ........................... 1.90734863281e-06sec
Move to Front decoding ..................... 0.000166177749634sec
Burrows-Wheeler inverse transformation ..... 1.19209289551e-05sec
Resulting uncompressed file is in ”../FichierTest/vide.txt.decoded”
./huffman -d ../FichierTest/zero.txt.encoded
Huffman decoding ........................... 0.00347995758057sec
Move to Front decoding ..................... 1.77122306824sec
Burrows-Wheeler inverse transformation ..... 10.0491089821sec
Resulting uncompressed file is in ”../FichierTest/zero.txt.decoded”
./huffman -d ../FichierTest/random.txt.encoded
Huffman decoding ........................... 0.0152530670166sec
Move to Front decoding ..................... 2.71150994301sec
Burrows-Wheeler inverse transformation ..... 14.5407299995sec
Resulting uncompressed file is in ”../FichierTest/random.txt.decoded”
./huffman -d ../FichierTest/France.txt
Fatal error: exception Failure(”input value: bad object”)
./huffman -d ../FichierTest/France.txt.encoded
Huffman decoding ........................... 0.0042769908905sec
Move to Front decoding ..................... 1.00814914703sec
Burrows-Wheeler inverse transformation ..... 5.16888594627sec
Resulting uncompressed file is in ”../FichierTest/France.txt.decoded”
./huffman -d ../FichierTest/nutellaraton.txt.encoded
Huffman decoding ........................... 0.0136139392853sec
Move to Front decoding ..................... 4.25166797638sec
Burrows-Wheeler inverse transformation ..... 24.8214280605sec
Resulting uncompressed file is in ”../FichierTest/nutellaraton.txt.decoded”
./huffman -d ../FichierTest/trop grand.txt
Fatal error: exception Failure(”input value: bad object”)
18
Téléchargement