RĪGAS TEHNISKĀ UNIVERSITĀTE
Datorzinātnes un
informācijas tehnoloģijas fakultāte
Informātikas un
programmēšanas katedra
Praktiskais darbs
Datu struktūras
“Hafmana kods”
1. Saturs
1. Saturs................................................................................................................................................ 2
2. Ievads............................................................................................................................................... 3
3. Biogrāfija.......................................................................................................................................... 3
4. Teorija............................................................................................................................................... 4
5. Realizācija........................................................................................................................................ 5
6. Realizācija uz Pascal......................................................................................................................... 9
7. Secinājumi........................................................................................................................................ 9
8.
Bibliogrāfiskais saraksts................................................................................................................... 9
2. Ievads
Hafmana
kods (Hafmana alogritms) – optimālas prefiksa kodēšanas adaptējamais mantkārīgs
algoritms* (Greedy algorithm) ar minimālo
pārpalikumu. Tika izstrādāts 1952. gadā Masačūsetsas tehnoloģiskajā
Universitātē. Mūsdienās to ļoti plaši pielieto arhivēšana un kriptogrāfijā.
Algoritms
sastāv no diviem etapiem:
·
Optimāla kodu
koka izbūve
·
Attēlojuma
kods-simbols izbūve uz koka pamata
3.
Biogrāfija
Algoritma autors - Davīds Hafmans (David Albert Huffman) (9.08.25 -
07.10.99)
Universitātes:
·
Ogajo
Universitāte
·
Masačūsetsas
Tehnoloģiskais Institūts
Sasniegumi:
·
1952. gadā
izveidoja prefiksa kodēšanas algoritmu ar minimālo pārpalikumu – Hafmana kodu.
·
1999. gadā
saņēma Ričarda Hemminga medali par ieguldījumu informācijas teorijā.
Dāvids Hafman piedzima 1925. gadā Ogajo, ASV. 18
gadu vecumā viņš guva bakalaura grādu elektrotehnikā Ogajo Universitātē. Vēlāk
viņš dienēja armijā kā virsnieks radara atbalstā grupā. Viņu daļas nodarbojas
ar Japānas un Ķīnas ūdeņu atbrīvošanu no mīnām pēc II.p.k. Pēc dienesta Dāvids
guva maģistra grādu Ogajo Universitātē un vēlāk doktora grādu Masačūsetsas
Tehnoloģijas Institūtā. Kaut vai Hafmans ir vairāk pazīstām dēļ savu algoritmu,
viņš izdarīja svarīgu ieguldījumu arī citās nozarēs (galvenokārt elektronikā).
Viņš ilgu laiku vadīja MTI datorzinātņu
katedru. 1974. gadā, būdams jau slavens profesors viņš atkāpās no amata.
1999. gada oktobrī, 74 gadu vecumā Dāvids Hafmans mira no vēža.
Haffmans guva virkni apbalvojumu:
·
1999 –
Ričarda Hemminga medaļu (Institute of Electrical and Electronics Engineers –
IEEE), par ieguldījumu informācijas teorijā.
·
1999- Louis E. Levy medali (Franklin Institute) par
viņa doktora darbu par virknes pārslēguma shēma.
·
1998 –
Apbalvojumu W. Wallace McDowell
·
1998 –
Apbalvojumu no IEEE datoru sabiedrības, par tehnoloģiskām inovācijām.
4.
Teorija
·
Aprakstītais
algoritms ir prefiksa algoritms – tas nozīme ka katru iekodētu simbolu var
interpretēt tikai vienā veida. To var paskaidrot uz piemēra – ja A ir
"0110" un B ir "01", tad saņemot šos simbolus pa bitam mēs nevarēsim pateikt
vai tas ir nākamais šī simbola skaitlis, vai arī nākamais simbols. Prefiksa
kodēšanā simbolu secība neatkārtojas tādā veidā lai tos varētu interpretēt
dažādi. To nodrošina binārais koks.
·
Minētājs
algoritms ir mantkārīgais algoritms (angl. Greedy algorithm) tas nozīme ka algoritms
meklē lokāli optimālos risinājumus katra etapā, pieņemot, ka arī kopumā
risinājums būs optimāls.
·
Adaptējamais
algoritms – algoritms kas mēģina sasniegt labākus rezultātus pastāvīgi
pielāgojoties ieejas datiem. Šādus algoritmus pielieto saspiešanā bez zudumiem.
5. Realizācija
1.
Pirmais ko ir
jāizdara kodējot failu pēc Hafmana koda ir izlasīt visu failu kopumā un
saskaitīt cik reizes atkārtojas katrs simbols (pēc pilnas ASCII tabulas). Ja
mēs skaitīsim visus simbolus, tad mums bus vienalga ko arhivēt: teksta failu
vai exe.
2.
Pēc tā ir
nepieciešams sastādīt tabulu ar katru simbolu, to skaitu un sakārtot to
dilstošā secībā. Piemēram:
Sākumā:
Simbols
|
A
|
B
|
C
|
D
|
E
|
F
|
Skaits
|
10
|
20
|
30
|
5
|
25
|
10
|
Rezultāts:
Simbols
|
C
|
E
|
B
|
F
|
A
|
D
|
Skaits
|
30
|
25
|
20
|
10
|
10
|
5
|
3.
Katru no
stabiņiem noformēsim mezglu veidā un saskaitīsim mezglus ar viszemāko
sastapšanas frekvenci. Mūsu gadījumā tas būs A un D mezgli. Jauna mezgla
sastapšanās frekvence – 15 (10+5). Tas ir kopējais simbolu A un D sastapšanās
biežums. (skat. Att. 5‑1).
4.
Tagad mēs atkal
meklējam mezglus ar vismazāko sastapšanās frekvenci, bet ignorējam A un D
mezglus, viņu vietā skatot jauno mezglu. Rezultāta meklētie mezgli ir F un A+D.
Atkārtosim iepriekšējo operāciju. (skat. Att. 5-2).
5.
Atkārtosim to
pašu operāciju, kamēr mēs neatradīsim koka sakni, kura summai jābūt vienādai ar
visu bloku summu. (skat. Att. 5-3).
6.
Tagad, kad
koks jau ir izveidots mēs varam sākt faila kodēšanu. Vienmēr ir jāsāk no saknes
un jākodē lapas pēc kārtas. Piemēram sāksim ar lapu C: ejot no saknes līdz lapai
mēs atcerēsimies katru kreiso pagriezienu ka 0 un katru labo kā 1. C gadījumā
mums ir divi kreisie pagriezieni, attiecīgi C kods bus vienāds ar
"00". Ņemsim lapu A, mums sanāk – kreisi – labi – kreisi – kreisi,
tas ir "0100". Tagad sastādīsim tabulu ar visiem simboliem un to
vērtībām.
Simbols
|
Kods
|
Atmiņas apjoms
|
C
|
00
|
2 bit
|
A
|
0100
|
4 bit
|
D
|
0101
|
4 bit
|
F
|
011
|
3 bit
|
B
|
10
|
2 bit
|
E
|
11
|
2 bit
|
7.
Katrs
simbols, kas agrāk sastāvēja no 8 bitiem, tagad ir krietni mazāks, attiecīgi
samazinājās arī faila izmērs.
Simbols
|
Sast.frekvence
|
Bitu skaits pēc
kodēšanas
|
Sākotnēji (bit)
|
Pēc kodēšanas (bit)
|
Izmēra samazinājums
|
C
|
30
|
2
|
240
|
60
|
180
|
A
|
10
|
4
|
80
|
40
|
40
|
D
|
5
|
4
|
40
|
20
|
20
|
F
|
10
|
3
|
80
|
30
|
50
|
B
|
20
|
2
|
160
|
40
|
120
|
E
|
25
|
2
|
200
|
50
|
150
|
Kopā:
|
800
|
240
|
560
|
8.
Sākotnējais
faila izmērs 100 byte – 800 bit. Saspiesta faila izmērs – 30 byte – 240 bit.
Fails ir saspiests uz 70%. Tas ir ļoti labs rezultāts, bet problēma ir tajā, kā
failu vel ir jāatkodē, un priekš tā ir vajadzīgs sākotnējais koks, kuru nāksies
glābāt tajā pašā failā, kas neizbēgami palielinās tā izmēru. Mūsu gadījumā
katrā mezglā atrodas 4 byte radītāju, tas nozīme ka pilnas tabulas apmērs būs
aptuveni 50 byte. Pieliksim pie 30 byte saspiestas informācijas 50 byte tabulas
un iegūsim 80 byte un 20% saspiešanu, kas ir pietiekoši labs rezultāts. Ko mēs
īstenība izdarījām – pārveidojam ASCII tabulu, lai tas kodi aizņemtu mazāk
vietās.
6. Realizācija uz Pascal
{$A+,B-,D+,E+,F-,G-,I-,L+,N-,O-,R+,S+,V+,X-}
{$M 16384,0,655360}
{* Hafmana koda
realizacija *}
Program Hafman;
Uses Crt,Dos,Printer;
Type PCodElement = ^CodElement;
CodElement = record
NewLeft,NewRight,
P0, P1 :
PCodElement;
LengthBiteChain : byte;
BiteChain : word;
CounterEnter : word;
Key : boolean;
Index : byte;
end;
TCodeTable = array [0..255] of
PCodElement;
Var CurPoint,HelpPoint,
LeftRange,RightRange : PCodElement;
CodeTable : TCodeTable;
Root : PCodElement;
InputF, OutputF, InterF : file;
TimeUnPakFile : longint;
AttrUnPakFile : word;
NumRead, NumWritten: Word;
InBuf
: array[0..10239] of byte;
OutBuf : array[0..10239] of byte;
BiteChain : word;
CRC,
CounterBite : byte;
OutCounter : word;
InCounter : word;
OutWord : word;
St : string;
LengthOutFile, LengthArcFile : longint;
Create : boolean;
NormalWork : boolean;
ErrorByte : byte;
DeleteFile : boolean;
procedure
ErrorMessage;
{ --- kludas
pazinojums --- }
begin
If ErrorByte <> 0 then
begin
Case ErrorByte of
2 : Writeln('File not found ...');
3 : Writeln('Path not found ...');
5 : Writeln('Access denied ...');
6 : Writeln('Invalid handle ...');
8 : Writeln('Not enough memory ...');
10 : Writeln('Invalid environment ...');
.
11 : Writeln('Invalid format ...');
18 : Writeln('No more files ...');
else Writeln('Error #',ErrorByte,' ...');
end;
NormalWork:=False;
ErrorByte:=0;
end;
end;
procedure ResetFile;
{ --- faila
atversana aktivacijai --- }
Var St : string;
begin
Assign(InputF, ParamStr(3));
Reset(InputF, 1);
ErrorByte:=IOResult;
ErrorMessage;
If NormalWork then Writeln('Pak file :
',ParamStr(3),'...');
end;
procedure
ResetArchiv;
{ --- arhiva
izveidosana vai atversana --- }
begin
St:=ParamStr(2);
If Pos('.',St)<>0 then
Delete(St,Pos('.',St),4);
St:=St+'.vsg';
Assign(OutputF, St);
Reset(OutPutF,1);
Create:=False;
If IOResult=2 then
begin
Rewrite(OutputF, 1);
Create:=True;
end;
If NormalWork then
If Create then Writeln('Create archiv :
',St,'...')
else Writeln('Open archiv : ',St,'...')
end;
procedure
SearchNameInArchiv;
{ --- faila
meklesana arhiva --- }
begin
Seek(OutputF,FileSize(OutputF));
ErrorByte:=IOResult;
ErrorMessage;
end;
procedure
DisposeCodeTable;
{ --- koda tabulas
iznicinasana --- }
Var I : byte;
begin
For I:=0 to 255 do Dispose(CodeTable[I]);
end;
procedure
ClosePakFile;
{ --- arhivejama
faila aizversana --- }
Var I : byte;
begin
If DeleteFile then Erase(InputF);
.
Close(InputF);
end;
procedure
CloseArchiv;
{ --- arhiva
aizversana --- }
begin
If FileSize(OutputF)=0 then Erase(OutputF);
Close(OutputF);
end;
procedure
InitCodeTable;
{ --- kodesanas
tablicas inicializacija --- }
Var I : byte;
begin
For I:=0 to 255 do
begin
New(CurPoint);
CodeTable[I]:=CurPoint;
With CodeTable[I]^ do
begin
P0:=Nil;
P1:=Nil;
LengthBiteChain:=0;
BiteChain:=0;
CounterEnter:=1;
Key:=True;
Index:=I;
end;
end;
For I:=0 to 255 do
begin
If I>0 then
CodeTable[I-1]^.NewRight:=CodeTable[I];
If I<255 codetable="" ewleft:="CodeTable[I];<o:p" then="">255>
end;
LeftRange:=CodeTable[0];
RightRange:=CodeTable[255];
CodeTable[0]^.NewLeft:=Nil;
CodeTable[255]^.NewRight:=Nil;
end;
procedure
SortQueueByte;
{ --- booble atlase
--- }
Var Pr1,Pr2 :
PCodElement;
begin
CurPoint:=LeftRange;
While CurPoint <> RightRange do
begin
If CurPoint^.CounterEnter >
CurPoint^.NewRight^.CounterEnter then
begin
HelpPoint:=CurPoint^.NewRight;
HelpPoint^.NewLeft:=CurPoint^.NewLeft;
CurPoint^.NewLeft:=HelpPoint;
If HelpPoint^.NewRight<>Nil then
HelpPoint^.NewRight^.NewLeft:=CurPoint;
CurPoint^.NewRight:=HelpPoint^.NewRight;
HelpPoint^.NewRight:=CurPoint;
If HelpPoint^.NewLeft<>Nil then
HelpPoint^.NewLeft^.NewRight:=HelpPoint;
If CurPoint=LeftRange then
LeftRange:=HelpPoint;
If HelpPoint=RightRange then
RightRange:=CurPoint;
CurPoint:=CurPoint^.NewLeft;
.
If CurPoint = LeftRange then
CurPoint:=CurPoint^.NewRight
else CurPoint:=CurPoint^.NewLeft;
end
else CurPoint:=CurPoint^.NewRight;
end;
end;
procedure
CounterNumberEnter;
{ --- frekvences
saskaitisana --- }
Var C : word;
begin
For C:=0 to NumRead-1 do
Inc(CodeTable[(InBuf[C])]^.CounterEnter);
end;
function
SearchOpenCode : boolean;
{ --- minimalo vertibu
meklesana --- }
begin
CurPoint:=LeftRange;
HelpPoint:=LeftRange;
HelpPoint:=HelpPoint^.NewRight;
While not CurPoint^.Key do
CurPoint:=CurPoint^.NewRight;
While (not (HelpPoint=RightRange)) and (not
HelpPoint^.Key) do
begin
HelpPoint:=HelpPoint^.NewRight;
If (HelpPoint=CurPoint) and
(HelpPoint<>RightRange) then
HelpPoint:=HelpPoint^.NewRight;
end;
If HelpPoint=CurPoint then
SearchOpenCode:=False else SearchOpenCode:=True;
end;
procedure
CreateTree;
{ --- frekvencu koka
izveidosana --- }
begin
While SearchOpenCode do
begin
New(Root);
With Root^ do
begin
P0:=CurPoint;
P1:=HelpPoint;
LengthBiteChain:=0;
BiteChain:=0;
CounterEnter:=P0^.CounterEnter +
P1^.CounterEnter;
Key:=True;
P0^.Key:=False;
P1^.Key:=False;
end;
HelpPoint:=LeftRange;
While (HelpPoint^.CounterEnter <
Root^.CounterEnter) and
(HelpPoint<>Nil) do
HelpPoint:=HelpPoint^.NewRight;
If HelpPoint=Nil then { pielikt beigas }
begin
Root^.NewLeft:=RightRange;
RightRange^.NewRight:=Root;
Root^.NewRight:=Nil;
RightRange:=Root;
end
.
else
begin { ielikt pirms HelpPoint }
Root^.NewLeft:=HelpPoint^.NewLeft;
HelpPoint^.NewLeft:=Root;
Root^.NewRight:=HelpPoint;
If Root^.NewLeft<>Nil then
Root^.NewLeft^.NewRight:=Root;
end;
end;
end;
procedure ViewTree(
P : PCodElement );
{ --- kodesanas
virknu pieskirsana lapam --- }
Var Mask,I : word;
begin
Inc(CounterBite);
If P^.P0<>Nil then ViewTree( P^.P0 );
If P^.P1<>Nil then
begin
Mask:=(1 SHL (16-CounterBite));
BiteChain:=BiteChain OR Mask;
ViewTree( P^.P1 );
Mask:=(1 SHL (16-CounterBite));
BiteChain:=BiteChain XOR Mask;
end;
If (P^.P0=Nil) and (P^.P1=Nil) then
begin
P^.BiteChain:=BiteChain;
P^.LengthBiteChain:=CounterBite-1;
end;
Dec(CounterBite);
end;
procedure
CreateCompressCode;
{ --- mainigo
anulesana un koka lasisana no augsas --- }
begin
BiteChain:=0;
CounterBite:=0;
Root^.Key:=False;
ViewTree(Root);
end;
procedure DeleteTree;
{ --- koka dzesana
--- }
Var P : PCodElement;
begin
CurPoint:=LeftRange;
While CurPoint<>Nil do
begin
If (CurPoint^.P0<>Nil) and
(CurPoint^.P1<>Nil) then
begin
If CurPoint^.NewLeft <> Nil then
CurPoint^.NewLeft^.NewRight:=CurPoint^.NewRight;
If CurPoint^.NewRight <> Nil then
CurPoint^.NewRight^.NewLeft:=CurPoint^.NewLeft;
If CurPoint=LeftRange then
LeftRange:=CurPoint^.NewRight;
If CurPoint=RightRange then
RightRange:=CurPoint^.NewLeft;
P:=CurPoint;
CurPoint:=P^.NewRight;
Dispose(P);
end
.
else CurPoint:=CurPoint^.NewRight;
end;
end;
procedure
SaveBufHeader;
{ --- bufera
ieraksta arhiva nos --- }
Type
ByteField = array[0..6] of byte;
Const
Header : ByteField = ( $56, $53, $31,
$00, $00, $00, $00 );
begin
If Create then
begin
Move(Header,OutBuf[0],7);
OutCounter:=7;
end
else
begin
Move(Header[3],OutBuf[0],4);
OutCounter:=4;
end;
end;
procedure
SaveBufFATInfo;
{ --- bufera
ieraksta informaciju par failuзапись --- }
Var I : byte;
St : PathStr;
R : SearchRec;
begin
St:=ParamStr(3);
For I:=0 to Length(St)+1 do
begin
OutBuf[OutCounter]:=byte(Ord(St[I]));
Inc(OutCounter);
end;
FindFirst(St,$00,R);
Dec(OutCounter);
Move(R.Time,OutBuf[OutCounter],4);
OutCounter:=OutCounter+4;
OutBuf[OutCounter]:=R.Attr;
Move(R.Size,OutBuf[OutCounter+1],4);
OutCounter:=OutCounter+5;
end;
procedure
SaveBufCodeArray;
{ --- saglabat
frekfencu sastapsanas masivu failaa --- }
Var I : byte;
begin
For I:=0 to 255 do
begin
OutBuf[OutCounter]:=Hi(CodeTable[I]^.CounterEnter);
Inc(OutCounter);
OutBuf[OutCounter]:=Lo(CodeTable[I]^.CounterEnter);
Inc(OutCounter);
end;
end;
.
procedure
CreateCodeArchiv;
{ --- saspiesanas
koda veidosana --- }
begin
InitCodeTable; { koda tabulas veidosana }
CounterNumberEnter; { sastapsanas frekvencu
skaitisana }
SortQueueByte; { sortesana pec sastapsanas biezuma }
SaveBufHeader; { salgabat arhiva nos bufera }
SaveBufFATInfo; { saglaba FAT informaciju par failu }
SaveBufCodeArray; { saglaba masivu ar sastapsanas frekvenci
failaa }
CreateTree; { frekvencu koka izveidosana }
CreateCompressCode; { saspiesta koda
veidosana }
DeleteTree; { frekvena koka dzesana }
end;
procedure
PakOneByte;
{ --- saspiesana un
parsutisana uz izejas buferu baitu --- }
Var Mask : word;
Tail : boolean;
begin
CRC:=CRC XOR InBuf[InCounter];
Mask:=CodeTable[InBuf[InCounter]]^.BiteChain
SHR CounterBite;
OutWord:=OutWord OR Mask;
CounterBite:=CounterBite+CodeTable[InBuf[InCounter]]^.LengthBiteChain;
If CounterBite>15 then Tail:=True else
Tail:=False;
While CounterBite>7 do
begin
OutBuf[OutCounter]:=Hi(OutWord);
Inc(OutCounter);
If OutCounter=(SizeOf(OutBuf)-4) then
begin
BlockWrite(OutputF,OutBuf,OutCounter,NumWritten);
OutCounter:=0;
end;
CounterBite:=CounterBite-8;
If CounterBite<>0 then
OutWord:=OutWord SHL 8 else OutWord:=0;
end;
If Tail then
begin
Mask:=CodeTable[InBuf[InCounter]]^.BiteChain
SHL
(CodeTable[InBuf[InCounter]]^.LengthBiteChain-CounterBite);
OutWord:=OutWord OR Mask;
end;
Inc(InCounter);
If (InCounter=(SizeOf(InBuf))) or
(InCounter=NumRead) then
begin
InCounter:=0;
BlockRead(InputF,InBuf,SizeOf(InBuf),NumRead);
end;
end;
procedure PakFile;
{ --- faila
saspiesana --- }
begin
ResetFile;
SearchNameInArchiv;
If NormalWork then
begin
BlockRead(InputF,InBuf,SizeOf(InBuf),NumRead);
OutWord:=0;
.
CounterBite:=0;
OutCounter:=0;
InCounter:=0;
CRC:=0;
CreateCodeArchiv;
While (NumRead<>0) do PakOneByte;
OutBuf[OutCounter]:=Hi(OutWord);
Inc(OutCounter);
OutBuf[OutCounter]:=CRC;
Inc(OutCounter);
BlockWrite(OutputF,OutBuf,OutCounter,NumWritten);
DisposeCodeTable;
ClosePakFile;
end;
end;
procedure
ResetUnPakFiles;
{ --- faila
atversana --- }
begin
InCounter:=7;
St:='';
repeat
St[InCounter-7]:=Chr(InBuf[InCounter]);
Inc(InCounter);
until InCounter=InBuf[7]+8;
Assign(InterF,St);
Rewrite(InterF,1);
ErrorByte:=IOResult;
ErrorMessage;
If NormalWork then
begin
WriteLn('UnPak file : ',St,'...');
Move(InBuf[InCounter],TimeUnPakFile,4);
InCounter:=InCounter+4;
AttrUnPakFile:=InBuf[InCounter];
Inc(InCounter);
Move(InBuf[InCounter],LengthArcFile,4);
InCounter:=InCounter+4;
end;
end;
procedure
CloseUnPakFile;
{ --- faila
aizversana --- }
begin
If not NormalWork then Erase(InterF)
else
begin
SetFAttr(InterF,AttrUnPakFile);
SetFTime(InterF,TimeUnPakFile);
end;
Close(InterF);
end;
procedure RestoryCodeTable;
{ --- koda tabulas
veidosana pec arhivu faila --- }
Var I : byte;
begin
InitCodeTable;
For I:=0 to 255 do
.
begin
CodeTable[I]^.CounterEnter:=InBuf[InCounter];
CodeTable[I]^.CounterEnter:=CodeTable[I]^.CounterEnter
SHL 8;
Inc(InCounter);
CodeTable[I]^.CounterEnter:=CodeTable[I]^.CounterEnter+InBuf[InCounter];
Inc(InCounter);
end;
end;
procedure UnPakByte(
P : PCodElement );
{ --- baita
atpakosana --- }
Var Mask : word;
begin
If (P^.P0=Nil) and (P^.P1=Nil) then
begin
OutBuf[OutCounter]:=P^.Index;
Inc(OutCounter);
Inc(LengthOutFile);
If OutCounter = (SizeOf(OutBuf)-1) then
begin
BlockWrite(InterF,OutBuf,OutCounter,NumWritten);
OutCounter:=0;
end;
end
else
begin
Inc(CounterBite);
If CounterBite=9 then
begin
Inc(InCounter);
If InCounter = (SizeOf(InBuf)) then
begin
InCounter:=0;
BlockRead(OutputF,InBuf,SizeOf(InBuf),NumRead);
end;
CounterBite:=1;
end;
Mask:=InBuf[InCounter];
Mask:=Mask SHL (CounterBite-1);
Mask:=Mask OR $FF7F; { visu bitu uzst
iznemot vecako }
If Mask=$FFFF then UnPakByte(P^.P1)
else UnPakByte(P^.P0);
end;
end;
procedure UnPakFile;
{ --- faila
atpakosana --- }
begin
BlockRead(OutputF,InBuf,SizeOf(InBuf),NumRead);
ErrorByte:=IOResult;
ErrorMessage;
If NormalWork then ResetUnPakFiles;
If NormalWork then
begin
RestoryCodeTable;
SortQueueByte;
CreateTree; { frekvencu koka veidosana }
CreateCompressCode;
CounterBite:=0;
.
OutCounter:=0;
LengthOutFile:=0;
While LengthOutFile
UnPakByte(Root);
BlockWrite(InterF,OutBuf,OutCounter,NumWritten);
DeleteTree;
DisposeCodeTable;
end;
CloseUnPakFile;
end;
{
------------------------- main text ------------------------- }
begin
DeleteFile:=False;
NormalWork:=True;
ErrorByte:=0;
WriteLn;
WriteLn('ArcHaf version 1.0 (c) Copyright VVS Soft Group, 1992.');
ResetArchiv;
If NormalWork then
begin
St:=ParamStr(1);
Case St[1] of
'a','A' : PakFile;
'm','M' : begin
DeleteFile:=True;
PakFile;
end;
'e','E' : UnPakFile;
else ;
end;
end;
CloseArchiv;
end.
7.
Secinājumi
Tagad izskatīsim maksimumu, kuru mēs varam
sasniegt ar dažādam kombinācijām no optimāla koka, kas nav simetrisks. Sanāks
ka mēs varam dabūt:
Simbolu Skaits
|
Bitu skaits
|
4
|
2
|
8
|
3
|
16
|
4
|
32
|
5
|
64
|
6
|
128
|
7
|
2
|
8
|
256
|
|
Iznāk 254 optimizēti simbolu kodi. Ir
nepieciešami kodi vēl 2 simboliem. Mēs varam paņemt 2 8 bit simbolus, lai
pilnība pārklāt ASCII tabulu. Sanāk kā mums ir 256 kombinācijas ar kuram mēs
varam kodēt baitu, no tām ir tikai 2 ar 8 bitiem. Ja mēs saskaitīsim bitu
skaitu, kas to ļauj, tad dabūsim 1554 bitus vai 195 baitus. Tas nozīmē, kā mēs
varam saspiest 256 byte līdz 195 vai arī uz 33%, tas nozīmē, ka ideāla gadījumā
Hafmana algoritms ļauj sasniegt 33% saspiešanu, kad tas tiek pielietots baitu
līmeni.
Visi
aprēķini tika veikti neprefikses Hafmana kodam, tas ir kodam, kuru nevar
identificēt viennozīmīgi. Piemēram kodu A – 01011 un B – 0101. Ja mēs saņemsim
šos kodus pa vienam bitam, tad saņemot
bitus 0101 mēs nevārēsim viennozīmīgi pateikt kuru no kodiem mēs saņemam, t.k.
Nākamais bits var būt gan turpinājums, gan nākamais kods.
Ir
nepieciešams piebilst, ka visvienkāršākais veids ka uztaisīt prefiksa kodu ir
parastais binārais koks. Ja uzmanīgi izpētīt iegūto koku, tad kļūs skaidrs ka
kodi tajā ir prefiksie.
Vēl
viena īpašība – Hafmana algoritms pieprasa lasīt ieejas failu divas reizes.
Pirmo reizi skaitot simbolus un otro reizi veicot kodēšanu.
Kopumā
var secināt kā Hafmana pats par sevi labi optimizē kodu, bet neļauj sasniegt
pietiekoši lielu saspiešanās pakāpi, bet kopā ar citiem algoritmiem
rezultativitāte ievērojami pieaug. Vienā no galvenām Hafmana koda priekšrocībām
ir algoritma adaptivitāte, tas nozīme, ka kods vienmēr ir optimāls, strādājot
ar jebkuru avotu.
8. Bibliogrāfiskais saraksts
* Mantkarīgs algaritms (Greedy algorithm) - algoritms
kas ļāuj atrāst lokāli optimālus risinājumus katrā etapā, pieļaujot, ka arī
gala risinājums būs optimāls.
Nav komentāru:
Ierakstīt komentāru