0% ont trouvé ce document utile (0 vote)
7 vues5 pages

TP 22 : Algorithme de Huffman en OCaml

Transféré par

floopsy61
Copyright
© All Rights Reserved
Nous prenons très au sérieux les droits relatifs au contenu. Si vous pensez qu’il s’agit de votre contenu, signalez une atteinte au droit d’auteur ici.
Formats disponibles
Téléchargez aux formats PDF, TXT ou lisez en ligne sur Scribd
0% ont trouvé ce document utile (0 vote)
7 vues5 pages

TP 22 : Algorithme de Huffman en OCaml

Transféré par

floopsy61
Copyright
© All Rights Reserved
Nous prenons très au sérieux les droits relatifs au contenu. Si vous pensez qu’il s’agit de votre contenu, signalez une atteinte au droit d’auteur ici.
Formats disponibles
Téléchargez aux formats PDF, TXT ou lisez en ligne sur Scribd

MP2I — 2022-2023 Perre Le Scornet Modue Agorthmque

TP 22 : Agorthme de Human —
compéments
Ce TP vent en compément du TP 22 sur ’agorthme de Human. Notamment, on va essayer de are de
ce TP queque chose d’utsabe dans a vrae ve (oué oué).
On reprend es mêmes types et notatons qu’au TP 22, notamment :

type arbre_code = OCam


| F of char
| N of arbre_code * arbre_code

Exercice 1 – Lecture de fchier non compressé octet par octet

En OCam,  est possbe de re et d’écrre un ficher non pas en tant que ficher texte, mas en e
manpuant drectement octet par octet. Or, quand on utse a oncton open_in : string ->
in_channel pour ouvrr un ficher, e ficher est ouvert dans un mode partcuer appeé mode
texte : dans ce mode, certans octets du ficher seront fitrés ou tratés de manère partcuère.
Ans, quand on utse input_line : in_channel -> string pour re une gne du ficher, on
saute es octets correspondants à un retour à a gne (qu dépendent égaement de ’édteur qu a
été utsé pour créer e ficher !). Pour réger ce probème, on utsera a oncton :

val open_in_bin : string -> in_channel OCam

qu est dsponbe dans a bbothèque standard Stdlib. On utsera aors a oncton input_byte
: in_channel -> int qu t un caractère du ficher en entrée, t un octet et e renvoe sous a
orme d’un enter entre 0 et 255. Lorsque ’on est arrvé à a fin du ficher en entrée, un appe à
input_byte èvera ’excepton End_of_file.
▶ Question 1 Écrre une oncton tableau_occurences : string -> int array qu prend
en entrée e nom d’un ficher (ou e chemn vers un ficher) et renvoe e tabeau du nombre
d’occurences de chaque caractère, stocké dans un tabeau de tae 256. On n’oubera pas de ermer
es fichers ouverts à ’ade de close_in : in_channel -> unit.
Voà ! On peut mantenant “charger” un ficher et cacuer son arbre de Human en réutsant
es autres onctons écrtes pendant e TP22.

Exercice 2 – Sérialisation de l’arbre de Human


S ’on veut utser ’agorthme de Human dans a vrae ve, on dot séraser ’arbre de Human
assocé, c’est-à-dre e représenter sous a orme d’une chaîne de caractères pour a stocker dans
e ficher compressé. On va utser a oncton suvante :

(N (g, d)) = 0 ⋅ (g) ⋅ (d)


(F c) = 1 ⋅ c
avec 0 ’octet nu (autrement dt, 00000000) et 1 ’octet représentant 1 en non sgné (autrement
dt, 00000001).
▶ Question 1 Séraser à a man ’arbre suvant :

N (N (F 12, N (F 7, F 8)), F 40) OCam

▶ Question 2 Déséraser a sute d’octets suvante :


0 0 1 17 1 18 0 1 1 0 1 30 1 20

1
MP2I — 2022-2023 Perre Le Scornet Modue Agorthmque

▶ Question 3 Détermner e nombre d’octets occupé par a sérasaton d’un arbre représentant
’encodage de a totaté des 256 caractères.
On peut mantenant re et écrre des arbres de Human dans des fichers. Pour a ecture, on
utsera toujours des fichers représentés par des in_channel. Pour écrre dans un ficher, on ut-
sera a oncton output_byte : out_channel -> int -> unit te que output_byte c k écrt
’octet k ∈ J0, 255K dans e ficher représenté par c, et a oncton open_out_bin : string ->
out_channel qu permet d’ouvrr un ficher en écrture (toujours dans un mode permettant
d’écrre n’mporte que octet, contrarement à open_out).

Remarque 1

Quand on exécute open_out sur un nom de ficher,  est créé s’ n’exste pas et écrase e
contenu du ficher précédent s’ exste déjà.

▶ Question 4 Écrre es onctons suvantes, réasant a sérasaton et dé-sérasaton d’un


arbre de Human :

val output_arbre : out_channel -> arbre_code -> unit OCam


val input_arbre : in_channel -> arbre_code

Un ficher compressé contendra donc d’abord a sérasaton de ’arbre de Human utsé pus
e texte encodé.

Exercice 3 – Écriture du fchier compressé bit par bit

L’agorthme de Human utse un codage bnare de ongueur varabe : cea mpque notam-
ment que ’on va devor écrre bt par bt dans e ficher, aors qu’on ne dspose que d’une oncton
permettant d’écrre un octet. Pour évter cet écue, on va devor are des chox : s e texte encodé

content  bts, on va ’écrre en ⌈ ⌉ octets et éventueement rempr es 0 à 7 bts restants du
8
derner octet par des bts nus. Par exempe, pour e codage suvant :

'a' 'b' 'c'


0 100 101

L’encodage du mot "abbaca" est aors 010010001010 qu sera encodée en es octets
0100100010100000, c’est-à-dre 72 et 160.
Pour passer d’un lux de bts à un ficher ben écrt octet par octet, on va utser un objet nter-
médare sur eque on va enfier es bts au ur et à mesure de ’encodage, et qu va écrre un octet
dès que 8 bts y ont été ajouté. On va utser e type suvant :

type out_channel_bits = { OCam


o_fichier : out_channel;
mutable o_accumulateur : int;
mutable o_bits_accumules : int
}

Le premer champ correspond au ficher où écrre. Le second content ’octet actueement


construt sous a orme d’un enter. Le derner champ correspond au nombre de bts attendant
d’être écrts. On aura donc :

o_accumulateur ∈ J0, 2o_bits_accumules − 1K

2
MP2I — 2022-2023 Perre Le Scornet Modue Agorthmque

▶ Question 1 Écrre une oncton open_out_bits : string -> out_channel_bits qu prend
en entrée un nom / chemn de ficher et ntase un enregstrement de type out_channel_bits
pour écrre dans ce ficher.
▶ Question 2 Écrre une oncton output_bit : out_channel_bits -> bool -> unit qu
prend en entrée un ficher de type out_channel_bits et un bt de type bool, et trate ’écrture de
ce bt en mettant à jour ’accumuateur et e nombre de bts accumués, et en écrvant eectve-
ment ’octet accumué s e nombre de bts accumués attent 8.
Le souc de cette représentaton est que es bts nus de rempssage du derner octet peuvent
rendre e code ambgu (c’est e cas de ’exempe précédent : est-ce que es 0 finaux sont des 'a' ou
juste des 0 ajoutés) ? Pour réger ce probème, on ajoute un octet suppémentare au ficher qu
content e nombre de bts nus ajoutés pour compéter e derner octet du texte encodé.
Par exempe, en reprenant ’octet précédent, au eu d’écrre 0100100010100000 pour encoder a
chaîne de caractères "abbaca", on écrra putôt es octets 010010001010000000000100, e derner
octet ndquant que ’avant-derner octet content 4 bts nus de rempssage.
▶ Question 3 Écrre une oncton close_out_bits : out_channel_bits -> unit qu écrt
éventueement es derners bts accumués en n’oubant pas es bts nus, écrt ’octet fina nd-
quant e nombre de bts nus ajoutés et erme e ficher. On pourra utser a oncton close_out
: out_channel -> unit qu est équvaente à close_in pour es lux de sorte.

Exercice 4 – Lecture du fchier compressé bit par bit

Pour re un ficher compressé, on a déjà codé a moté des onctons nécessares : a oncton
input_arbre : in_channel -> arbre_code permet de re octet par octet notre ficher pour en
dédure son arbre de Human. On a quand même beson d’un type smare à out_channel_bits
permettant de re un ficher bt par bt :

type in_channel_bits = { OCam


i_fichier : in_channel;
mutable i_accumulateur : int;
mutable i_bits_accumules : int;
i_taille : int
}

— i_fichier est e lux d’entrée créé à partr d’un ficher,


— i_accumulateur content ’octet en tran d’être u (à chaque octet u dans e ficher,  est
ntasé à cet octet, pus  décroît jusqu’à attendre 0 au ur et à mesure que ’on t des
bts),
— i_bits_accumules content e nombre de bts restant à re dans i_accumulateur,
— i_taille content a tae du ficher, en nombre d’octets.
Les onctons suvantes permettent d’ouvrr et ermer un ficher en utsant ce type :

3
MP2I — 2022-2023 Perre Le Scornet Modue Agorthmque

let open_in_bits fn = OCam


let fichier = open_in_bin fn in
{
i_fichier = fichier;
i_accumulateur = 0;
i_bits_accumules = 0;
i_taille = in_channel_length fichier
(* voilà à quoi sert [in_channel_length], que les utilisateurs de
↪ VSCode / Codium doivent avoir déjà vu... *)
}

let close_in_bits f = close_in f.i_fichier

▶ Question 1 Écrre a oncton input_bit : in_channel_bits -> bool qu permet de re
un bt du lux en entrée. On aura beson de a oncton pos_in : in_channel -> int qu renvoe
a poston actuee du lux d’entrée dans e ficher, ce qu permet de gérer e cas du derner octet du
ficher.

Exercice 5 – Compression et décompression d’un octet

On peut mantenant écrre es onctons de compresson et décompresson que ’on a déjà écrt sur
es chaînes de caractères dans e TP 22, mas cette os-c en manpuant drectement es fichers
octet par octet.
▶ Question 1 Écrre a oncton de type :

compresse_byte : out_channel_bits -> tableau_code -> int -> unit OCam

qu prend en entrée e lux de sorte sur eque écrre, un tabeau de codes (comme ceu cacué
par a oncton creer_tableau du TP 22), un octet représenté par enter entre 0 et 255 et écrt a
sére de bts correspondante à cet octet dans e lux de sorte.
▶ Question 2 Écrre une oncton de type :

decompresse_byte : in_channel_bits -> arbre_code -> int OCam

qu décode un octet du texte encodé dsponbe dans e lux d’entrée ourn grâce à ’arbre de
Human égaement ourn en entrée, et e renvoe.

Exercice 6 – On met tout ensemble


Manque pus que deux onctons, ’une pour compresser un ficher et ’autre pour e décompres-
ser !
▶ Question 1 Écrre une oncton compresse_fichier : string -> string -> unit qu
prend en entrée un nom / chemn de ficher d’entrée, un nom / chemn de ficher de sorte et
compresse e premer en nscrvant e résutat dans e second.
▶ Question 2 Écrre de même une oncton decompresse_fichier : string -> string ->
unit qu eectue a décompresson.
Le code suvant, pacé à a fin de votre ficher, devrat vous permettre de comper un exécutabe
compressant ou décompressant un ficher ourn en argument :

4
MP2I — 2022-2023 Perre Le Scornet Modue Agorthmque

let main () = OCam


let error_and_exit () =
let invocation = [Link].(0) in
[Link] "Usage : \n";
[Link] "%s compress <input-file> <compressed-output-file>\n"
↪ invocation;
[Link] "%s decompress <compressed-file> <output-file>\n"
↪ invocation;
exit 1 in
if [Link] [Link] < 4 then error_and_exit ();
let commande = [Link].(1) in
let nom_in = [Link].(2) in
let nom_out = [Link].(3) in
if commande = "compress" then compresse_fichier nom_in nom_out
else if commande = "decompress" then decompresse_fichier nom_in nom_out
else error_and_exit ()

let () = main ()

Vous aimerez peut-être aussi