Vớ dụ về cõy nhị phõ n mó hoỏ Huffman (Đọc thờm)

Một phần của tài liệu giáo trình cấu trúc dữ liệu (Trang 80 - 88)

b. Cài đặt bằng con trỏ liờn kết.

5.6.Vớ dụ về cõy nhị phõ n mó hoỏ Huffman (Đọc thờm)

Giả sử ta cú một thụng bỏo đú là một chuỗi cỏc kớ tự, trong đú mỗi kớ tự xuất hiện độc lập với cựng một xỏc suất tại bất kỳ vị trớ nào trong thụng bỏo. Mục đớch của chỳng ta muốn mó hoỏ chuỗi thụng bỏo này thành một chuỗi cỏc bớt 0,1. Tất nhiờn việc mó hoỏ phải đảm bảo cú thể giải mó trở

lại được chuỗi thụng bỏo ban đầu. Ta thấy cú một tớnh chất mà phương phỏp mó hoỏ phải thoả món đú là: mó hoỏ của mỗi kớ tự khụng được là tiền tố của chuỗi mó hoỏ của kớ tự khỏc, chẳng hạn a được mó thành 10, b

được mó thành 1011 là khụng thể được vỡ khi giải mó sẽ nhầm. Ta gọi tớnh

chất này là tớnh chất tiền tố. Hơn nữa ta phải tỡm bộ mó tiết kiệm nhất, tức là thụng bỏo được mó thành ớt bớt nhất cú thể được.

Vớ dụ:

Thụng bỏo gồm 5 kớ tự a, b, c, d, e xuất hiện với xỏc suất tương ứng là: 0.10, 0.40, 0.12, 0.18, 0.20. Cú thể mó hoỏ như sau:

ký hiệu xỏc suất mó cỏch 1 mó cỏch 2 a .10 000 000 b .40 001 11 c .12 010 01 d .18 011 001 e .20 100 10

Với cỏch mó húa 1 ta dễ dàng giải mó cho một chuổi cỏc bớt 0,1. chẳng hạn chuỗi 000001010 sẽ cho kết quả abc.

Ta cũng dễ dàng kiểm tra cỏch mó hoỏ 2 là hợp lệ (thoả món tớnh chất tiền tố). Cỏch mó hoỏ 1 cho độ dài mó trung bỡnh của một kớ tự là 3, cỏch mó hoỏ 2 cho độ dài trung bỡnh mó 2.24 (vỡ 3*0.10 + 2*0.40 + 2*0.12 + 3*0.18 + 2* 0.20 = 2.24)

Cú cỏch mó hoỏ nào cho độ dài mó trung bỡnh nhỏ hơn 2.24 khụng? Một phương phỏp để tỡm lời giải tối ưu gọi là giải thuật Huffman:

* Giải thuật Huffman

Thành lập cõy nhị phõn từ tập hợp cỏc kớ hiệu trong thụng bỏo, mỗi kớ hiệu là một nỳt lỏ của cõy. Cỏch thành lập cõy như sau:

Chọn hai nỳt a,b cú xỏc suất nhỏ nhất trong tập hợp cỏc nỳt, giả sử xỏc suất nỳt a nhỏ hơn hoặc bằng xỏc suất nỳt b. Thành lập cõy nhị phõn cú nỳt gốc x, con trỏi là a, con phải là b. Nỳt x cú xỏc suất bằng tổng xỏc suất của a và b.

Tập hợp cỏc nỳt bõy giờ là cỏc nỳt cũn lại (đó loại bỏ a,b) và nỳt x. Lặp lại một cỏch đệ qui quỏ trỡnh trờn tập hợp đang xột cho đến khi tập này chỉ cũn lại một nỳt.

Mó của a, b sẽ tỡm được bằng cỏch lấy mó của x nối thờm 0 cho a và 1 cho b. Mó của nỳt gốc là rỗng.

Như vậy thực chất quỏ trỡnh trờn là ta xõy dựng một cõy nhị phõn từ tập hợp cỏc ký tự muốn mó hoỏ, cuối cựng ta được một cõy nhị phõn cú lỏ là cỏc ký tự đú. Mó của một ký tự là một đường đi trờn cõy từ gốc đến lỏ chứa kớ tự, với 0 đi sang trỏi cũn 1 đi sang phải. í tưởng của giải thuật mó hoỏ cũng hết sức đơn giản, ta tỡm bộ mó cho cỏc kớ tự sao cho cỏc kớ tự cú tần suất xuất hiện cao (xỏc suất xuất hiện là lớn) sẽ được mó ngắn (gần với gốc) để độ dài trung bỡnh để mó hoỏ một kớ tự là nhỏ nhất.

* Cài đặt giải thuật Huffman

Dưới đõy là một chương trỡnh hoàn chỉnh để cỏc bạn cú thể cài đặt và chạy thử. Ở đõy, chỳng tụi cài đặt bằng mảng (dựng con nhỏy) nhưng cỏc bạn cú thể dể dàng chuyển sang cài đặt bằng con trỏ.

const maxnode=100;

Type

TREE= array [1..maxnode] of record

leftchild: integer; rightchild: integer; parent: integer;

end;

ALPHABET= array[1..maxnode] of record

symbol: char;

probability: real;

leaf: integer; {mỗi kớ tự kết hợp với một nỳt lỏ của cõy}

end;

FOREST= array[1..maxnode] of record

weight: real; root: integer;

end; (adsbygoogle = window.adsbygoogle || []).push({});

var

A:ALPHABET; F:FOREST;

i,lastnode,lasttree,n,root:integer; s:string;

procedure initialize;{ Khởi tạo giỏ trị ban đầu }

var i: integer;

begin

write('Cho so nut ban dau?'); readln(lasttree);

for i:=1 to lasttree do begin

write('ky hieu cua nut thu ',i,' = '); readln(A[i].symbol);

write('xac suat cua nut thu ',i,' = '); readln(A[i].probability);

A[i].leaf:=i;

{hiện tại, mỗi nỳt là một cõy trong forest}

F[i].weight:=a[i].probability; F[i].root:=i; T[i].leftchild:=0; T[i].rightchild:=0; T[i].parent:=0; end; end;

{ Tỡm hai cõy cú trọng lượng bộ nhất}

procedure lightOnes( var least,second: integer);

var i:integer; begin if F[1].weight<=F[2].weight then begin least:=1; second:=2; end else begin least:=2;

second:=1;

end;

for i:=3 to lasttree do

if F[i].weight< F[least].weight then begin

second:=least; least:=i;

end

else

if F[i].weight<F[second].weight then second:=i;

end;

{ tạo cõy mới từ hai cõy cú trọng lượng bộ nhất }

Function create(lefttree,righttree: integer): integer;

begin

{cấp phỏt gốc mới}

lastnode:=lastnode+1; (adsbygoogle = window.adsbygoogle || []).push({});

{cho hai con trỏ của nỳt gốc trỏ hai nỳt gốc của hai cõy con}

T[lastnode].leftchild:=F[lefttree].root; T[lastnode].rightchild:=F[righttree].root;

{con trỏ trỏ nỳt cha của nỳt gốc = null}

T[lastnode].parent:=0;

{nỳt gốc mới là nỳt cha của hai nỳt gốc của hai cõy con}

T[F[lefttree].root].parent:=lastnode; T[F[righttree].root].parent:=lastnode; create:=lastnode;

end;

{ Giải thuật HUFFMAN }

procedure huffman(var root:integer);

var i,j:integer; newroot:integer; begin {lặp cho đến khi chỉ cũn một nỳt} while lasttree>1 do begin

lightones(i,j);{tỡm hai nỳt bộ nhất} newroot:=create(i,j);

{tạo cõy từ hai nỳt bộ nhất}

{trọng số nỳt mới bằng tổng trọng số của hai nỳt con}

F[i].weight:=F[i].weight+F[j].weight; F[i].root:=newroot; F[j]:=F[lasttree]; lasttree:=lasttree-1; end; root:=newroot; end; { Mó hoỏ một ký tự thành cỏc bit 0,1 } function encodeOneChar(ch:char):string; var i,j,l:integer; S_encode:string; begin

{khởi tạo chuỗi mó rỗng} S_encode:='';

i:=0;

{tỡm kớ tự cần mó hoỏ trong bảng Alphabet}

repeat

i:=i+1

until (i>n) or (A[i].symbol=ch);{n là số kớ tự trong bảng}

{nếu tỡm thấy}

if i<=n then

{lần ngược từ lỏ chứa kớ tự này lờn gốc cõy để tỡm chuỗi bớt 0,1 nếu nỳt nằm bờn trỏi thỡ thờm 0, nằm bờn phải thỡ thờm 1}

repeat

j:=T[i].parent; { lấy nỳt cha của nỳt i}

if T[j].leftchild=i then

S_encode:=’0’ + s_encode

else S_encode:=’1’+ S_encode;

i:=j;

else (adsbygoogle = window.adsbygoogle || []).push({});

begin

{khụng tỡm thấy kớ tự muốn mó hoỏ trong bảng kớ tự} writeln(' ký tự ',ch,' sai ');

S_encode:=S_encode+'*';

end;

encodeOneChar:=S_encode;

end;

{ Mó hoỏ một chuỗi ký tự thành chuỗi bớt 0,1 }

function encode(s:string):string;

var s_encode:string;

begin

s_encode:='';

for i:=1 to length(s) do

s_encode:=s_encode+encodeOneChar(s[i]); encode:=S_encode;

end;

{ Giải mó một chuỗi bớt 0,1 thành chuỗi ký tự thụng thường a,b,c}

function decode(s:string):string;

var S_decode: string; i,curr:integer; err:boolean; begin i:=1; S_decode:=''; Err:=false;

while (i<= length(s)) and ( not Err ) do

begin

curr:=root;

while ((T[curr].leftchild<>0) or

(T[curr].rightchild<>0))and (not Err) do begin

if s[i]='0' then

curr:=T[curr].leftchild

else Err:=true

else if s[i]='1' then

If t[curr].rightchild<>0

then

curr:=T[curr].rightchild

else Err:=true else Err:=true; i:=i+1 end; S_decode:=S_decode+A[curr].symbol end; if Err then begin (adsbygoogle = window.adsbygoogle || []).push({});

writeln(' Lỗi trong chuỗi muốn giải mó '); S_decode:=''; end; decode:=S_decode; end; {chương trỡnh chớnh } BEGIN initialize; lastnode:=lasttree; n:=lasttree;

{xõy dựng cõy Huffman} huffman(root);

{kiểm tra giải thuật mó hoỏ}

repeat

write('cho chuỗi ký tự muốn mó hoỏ '); readln(s);

writeln(encode(s));

write('cho chuỗi bớt 0,1 để giải mó '); readln(s);

until s='';

END.

Một phần của tài liệu giáo trình cấu trúc dữ liệu (Trang 80 - 88)