1. Trang chủ
  2. » Luận Văn - Báo Cáo

Thuật toán nén LZW

14 1,2K 5
Tài liệu đã được kiểm tra trùng lặp

Đang tải... (xem toàn văn)

Tài liệu hạn chế xem trước, để xem đầy đủ mời bạn chọn Tải xuống

THÔNG TIN TÀI LIỆU

Thông tin cơ bản

Tiêu đề Thuật toán nén LZW
Người hướng dẫn Thầy Nguyễn Lê Anh
Trường học Trường Đại Học
Thể loại Bài phân tích
Thành phố Việt Nam
Định dạng
Số trang 14
Dung lượng 91 KB

Các công cụ chuyển đổi và chỉnh sửa cho tài liệu này

Nội dung

Ngày nay, cùng với sự phát triển không ngừng của khoa học và công nghệ thì máy tính đóng vai trò không thể thiếu trong cuộc sống xã hội loài người. Việc trao đổi thông tin của con người trong tất cả các ngành, các lĩnh vực của đời sống ngày càng trở nên cấp thiết và quan trọng, chính vì thế mà các thiết bị thông tin mới liên tục ra đời nhằm đáp ứng các yêu cầu này. Tuy nhiên, vì một số phần mềm đòi hỏi rất nhiều bộ nhớ để hoạt động trao đổi thông tin nên người ta đã nghĩ ra một phương pháp nhằm giải quyết vấn đề này, đó là phương pháp nén dữ liệu mà vẫn bảo toàn thông tin. Nén dữ liệu là một kỹ thuật quan trọng trong rất nhiều lĩnh vực khác nhau. Chính nhờ có kỹ thuật nén dữ liệu mà ngày nay chúng ta có những phương tiện truyền thông hiện đại phục vụ cho cuộc sống như truyền hình cáp, điện thoại, thư điện tử ... và rất nhiều khía cạnh khác. Do đó kỹ thuật nén dữ liệu ngày càng được quan tâm và phát triển nhiều hơn. ở Việt Nam, hầu hết các trường Đại học đều quan tâm đến việc nén dữ liệu và điều này được thể hiện ở việc đưa kỹ thuật nén trở thành môn học chính thức trong giai đoạn chuyên ngành . Trong phạm vi môn học “ Mã - mã nén” . Tôi đưa ra bài phân tích trình LZW 15 nhằm mô phỏng thuật toàn kỹ thuật nén dữ liệu. Tuy nhiên do trình độ còn hạn chế, thời gian và kinh nghiệm chưa nhiều, nên bài phân tích này không thể tránh khỏi sự sai sót trong quá trình phân tích. Do vậy tôi rất mong được sự quan tâm tham gia góp ý Thầy Cô cũng như cùng toàn thể các bạn Sinh Viên để bài phân tích này rõ dàng hơn.

Trang 1

LỜI NÓI ĐẦU

Ngày nay, cùng với sự phát triển không ngừng của khoa học và công nghệ thì máy tính đóng vai trò không thể thiếu trong cuộc sống xã hội loài người

Việc trao đổi thông tin của con người trong tất cả các ngành, các lĩnh vực của đời sống ngày càng trở nên cấp thiết và quan trọng, chính vì thế mà các thiết bị thông tin mới liên tục ra đời nhằm đáp ứng các yêu cầu này Tuy nhiên, vì một số phần mềm đòi hỏi rất nhiều bộ nhớ để hoạt động trao đổi thông tin nên người ta đã nghĩ ra một phương pháp nhằm giải quyết vấn đề này, đó là phương pháp nén dữ liệu mà vẫn bảo toàn thông tin

Nén dữ liệu là một kỹ thuật quan trọng trong rất nhiều lĩnh vực khác nhau Chính nhờ có kỹ thuật nén dữ liệu mà ngày nay chúng ta có những phương tiện truyền thông hiện đại phục vụ cho cuộc sống như truyền hình cáp, điện thoại, thư điện tử và rất nhiều khía cạnh khác Do đó kỹ thuật nén dữ liệu ngày càng được quan tâm và phát triển nhiều hơn ở Việt Nam, hầu hết các trường Đại học đều quan tâm đến việc nén dữ liệu và điều này được thể hiện ở việc đưa kỹ thuật nén trở thành môn học chính thức trong giai đoạn chuyên ngành

Trong phạm vi môn học “ Mã - mã nén” Tôi đưa ra bài phân tích trình LZW 15 nhằm mô phỏng thuật toàn kỹ thuật nén dữ liệu

Tuy nhiên do trình độ còn hạn chế, thời gian và kinh nghiệm chưa nhiều, nên bài phân tích này không thể tránh khỏi sự sai sót trong quá trình phân tích Do vậy tôi rất mong được sự quan tâm tham gia góp ý Thầy Cô cũng như cùng toàn thể các bạn Sinh Viên để bài phân tích này rõ dàng hơn

Cuối cùng Em xin chân thành cảm ơn thày Nguyễn Lê Anh đã hướng dẫn và giảng dạy Em trong thời gian qua

Trang 2

Thuật toán nén LZW Bước 1 Cắt văn bản mới thành các đoạn copy nếu bảng chữ cái có m chữ thì các chữ

cái là m đoạn copy đầu tiên được đánh số từ 0 đến m-1

Bước 2 Bỏ tất cả phần chữ thu được mã nén.

Lưu ý rằng các đoạn copy lần lượt được tạo ra và phần số của nó luôn nhỏ hơn số hiệu cột mà nó đứng

Thuật toán giải nén LZW.

Bắt đầu là các cột đầu tiên (trong ví dụ là cột thứ 2) lặp lại thao tác sau cho đến hết Lấy hai số liên tiếp của bản mã ví dụ là X, Y thay nó về dạng X+? và Y+$ Trong đó

kí tự đầu tiên của Y+$ là kí tự cuối cùng của X+? Dấu ? và $ là thay cho một kí tự chưa biết Vì X và Y không thể lớn hơn chỉ số cột mà nó đứng cho nên chúng ta hoàn toàn tìm được giá trị đoạn copy ứng với cột có chỉ số X, Y và thay đoạn copy vào X+?

và Y+$ tương ứng Giá tri ? là kí tự đầu của Y+$ cho nên luôn luôn xác định Như thế chúng ta tìm được X+?

Ví dụ Nén theo LZW

Bước 1 aabababaaababb

thay a→0

được 0abababaaababb từ điển

đoạn copy mới aabababaaababb

Bước 2 aabababaaababb

thay a→0

được 00bababaaababb

từ điển

đoạn copy mới aabababaaababb

Bước 3 00bababaaababb

thay b→1

được 001ababaaababb

từ điển

đoạn copy mới aabababaaababb

Bước 4 001ababaaababb

thay ab→3

được 0013abaaababb

0+a

0+a

0+a

0+b

Trang 3

từ điển

đoạn copy mới aabababaaababb

aabababaaababb

Bước 5 0013abaaababb

thay aba→5

được 00135aababb

từ điển

đoạn copy mới aabababaaababb

Bước 6 00135aababb

thay aa→2

được 001352babb

từ điển

đoạn copy mới aabababaaababb

Bước 7 001352babb

thay ba→4

được 0013524bb

từ điển

đoạn copy mới aabababaaababb

THUẬT TOÁN LZW

Trong LZW thì token chỉ có index Để làm việc này, từ điển khi được khởi tạo đã gồm tất cả các ký tự đơn lẻ cho nên nó luôn được tìm thấy trong từ điển mặc dù có thể trước đó chưa xuất hiện trong văn bản

Thuật toán nén cho LZW:

0+a

0+b

1+a

0+a

0+b

1+a

0+a

0+b

1+a

3+a

Trang 4

Khi mới bắt đầu, từ điển đã gồm tất cả các ký tự đơn lẻ.

Xâu hiện tại bắt đầu có độ dài 1 Mỗi khi đọc thêm 1 ký tự thì nó được thêm vào xâu hiện tại Nếu xâu hiện tại còn trùng với một trong các phrase đã có thì quá trình

cứ tiếp tục

Khi không có phrase trong từ điển trùng với xâu hiện tại nữa thì :

chúng ta cho ra index là chỉ số xâu trước đó (không kể ký tự vừa đọc vào) trong từ điển

thêm xâu hiện tại (bao gồm cả ký tự vừa đọc vào) vào trong từ điển

bắt đầu một xâu mới bằng ký tự vừa đọc vào

Pseudocode nén của LZW:

Hai lệnh cuối cùng là để xử lý khi hết file, lúc đó chưa có phrase mới nên không có lệnh add_to_dictionary() Cột đầu tiên là string1 (biến của lệnh add_to_dictionary) bỏ bớt đi ký tự đầu (ký tự này có do các lệnh string1[0]=character; string1[1]=’\0’; ở phần else {} của bước trước), đây thực chất là các ký tự đã được đọc bởi lệnh

character=getc(input) Cột thứ hai là index của phrase trong từ điển (ở đây khi phrase

chỉ có 1 ký tự thì chúng ta sử dụng chính ký tự này thay cho index, đúng ra là phải dùng hàm ascii (ký tự)) Cột thứ 3 là biến (string1 + character) trong lệnh add_to_dictionary (string1 + character) kèm theo index của nó trong từ điển

Ví dụ: Mỗi dòng của bảng sẽ ứng với một lần thực hiện vòng lặp

while (!feof(input)) (trừ dòng đầu tiên)

string1[0]=getc(input);

string1[1]=’\0’;

while (!feof(input)) {

character=getc(input);

if in_dictionary(string1+character)

{string1=string1+character;}

else {

code=look_up_dictionary(string1);

output_code(code);

add_to_dictionary(string1+character);

string1[0]=character;

string1[1]=’\0’;

} code=look_up_dictionary(string1);

output_code(code);

Trang 5

Input String: “ WED WE WEE WEB WET”

Từ thuật toán nén ta thấy rằng:

- index của string1 ở bước hiện tại được đưa vào tệp nén.

- phrase được thêm vào từ điển là string1 ở bước hiện tại + character, trong đó

character là ký tự đầu của string1 ở bước sau

Thuật toán giải nén như sau:

Đầu tiên, đọc một index vào và tìm phrase tương ứng với nó trong từ điển Đưa phrase

này ra tệp (string1 ở bước trước).

Vòng lặp:

đọc index tiếp theo, tra từ điển để tìm string1 ở bước hiện tại, đưa nó vào tệp giải nén thêm (string1 ở bước trước + ký tự đầu của string1 ở bước hiện tại) vào từ điển

Trong đoạn pseudocode sau thì string2 chính là ‘string1 ở bước trước’:

Ví dụ của việc giải nén:

Trong bảng sau đây, mỗi dòng sẽ tương ứng với một lần thực hiện vòng lặp

while{}, riêng dòng đầu tiên là do các lệnh

string2[0]=input_bits();

string2[1]=’\0’;

putc(string2[0], output);

Cột thứ nhất (I) là kết quả của lệnh code =input_bits();

Cột thứ hai (II) là kết quả của các lệnh

string1=dictionary_lookup(code);

fputs(string1, output);

string2[0]=input_bits();

string2[1]=’\0’;

putc(string2[0], output);

while ((code=input_bits() )!=EOF) {

string1=dictionary_lookup(code);

fputs(string1, output);

add_to_dictionary(string2+string1[0]);

strcpy(string2, string1);}

Trang 6

Cột thứ ba (III) là biến string2 trong lệnh add_to_dictionary(string2+ string1[0]).

Nó bằng với cột thứ hai (string1) ở bước trước do lệnh strcpy(string2, string1)

Cột thứ tư (IV) là ký tự đầu của cột thứ II (string1[0]).

Cột thứ năm (V) là biến (string2+string1[0]) trong lệnh add_to_dictionary(string2+ string1[0]).

Input Codes: “ WED<256>E<260><261><257>B<260>T”

Một điều cần chú ý khi nén bằng LZW: nó thêm phrase vào từ điển trước khi toàn

bộ phrase này được nén, cụ thể là ký tự cuối của phrase chỉ được xử lý ở lần lặp sau

Nếu vì một lý do nào đó mà trình nén sử dụng ngay lập tức phrase này thì khi giải nén

sẽ có vấn đề là gặp phải code của một phrase mà phrase này lại chưa xuất hiện trong

từ điển (phrase này còn thiếu một ký tự cuối là ký tự đầu của xâu được giải mã tiếp theo Rất tiếc là điều này có thể xảy ra Điều này chỉ xảy ra khi trong dữ liệu vào có đoạn CHARACTER.STRING.CHARACTER.STRING.CHARACTER Trong ví dụ sau CHARACTER=’I’, STRING=’WOMBAT’ Giả sử xâu ‘IWOMBAT’ đã có trong

từ điển (mã là 300) còn ‘IWOMBATI’ thì chưa có Như vậy khi gặp đoạn dữ liệu‘IWOMBATIWOMBATI’ trình nén sẽ cho ra mã 300 và thêm ‘IWOMBATI’ vào

từ điển(với mã 400 chẳng hạn) Và sau đó nó sử dụng ngay mã 400 cho đoạn tiếp theo, tức là cả đoạn dữ liệu trên sẽ cho ra mã <300><400> Bây giờ chúng ta hãy theo dõi quá trình giải nén Sau khi thay <300> bằng ‘IWOMBAT’ thì trình giải nén gặp

mã 400 Nhưng khi đó trong từ điển của trình giải nén còn chưa có phrase

‘IWOMBATI’ vì còn thiếu chữ‘I’ ở cuối Cho nên trong đoạn pseudocode trên, lệnh

string1=dictionary_lookup(code) không phải lúc nào cũng thành công Nhưng nó cũng chỉ không thành công trong trường hợp gặp phải dữ liệu có dạng như vừa mô tả, vì để

tạo thành pharase mới trong từ điển chúng ta cũng chỉ thiếu có 1ký tự cuối và ký tự này xuất hiện ở ngay đầu của đoạn dữ liệu tiếp theo Cho nên chúng ta cần phải sửa lại như sau:

old_string[0]=input_bits();

old_string[1]=’\0’;

putc(old_string[0], output);

while ((new_code=input_bits())!=EOF) {

new_string=dictionary_lookup(new_code);

if (new_string==NULL) {

strcpy(new_string, old_string);

append_character_to_string(new_string, new_string[0]);

} fputs(new_string, output);

append_character_to_string(old_string, new_string[0]);

add_to_dictionary(old_string);

strcpy(old_string, new_string);

}

Trang 7

Chương trình LZW15:

/* LZW15V.C : realization of Lempel-Ziv*/

#include "bitio.c"

void usage_exit(char *prog_name);

void CompressFile(FILE input, BIT_FILE *output, int argc, char *argv[]);

void ExpandFile(BIT_FILE *input, FILE *output, int argc, char *argv[]);

unsigned int decode_string(unsigned int count, unsigned int code);

unsigned int find_child_node(int parent_code, int child_character);

void InitializeStorage(void);

void InitializeDictionary(void);

char *CompressionName="LZW 15 Bit Variable Rate Encoder ";

char *Usage="in-file out-file \n\n";

void usage_exit(char *prog_name)

{

char *short_name;

char *extension;

short_name = strrchr(prog_name,'\\');

if (short_name == NULL) short_name=strrchr(prog_name,':');

if (short_name!= NULL) short_name++;

else short_name=prog_name;

extension=strrchr(short_name,'.');

if (extension != NULL) *extension='\0';

printf("\nUsage : %s %s \n",short_name,Usage);

exit(0);

}

/*===============================*/

#define BITS 15

#define MAX_CODE ((1<<BITS)-1)

#define TABLE_SIZE 35023L

#define TABLE_BANKS ((TABLE_SIZE >> 8)+1)

#define END_OF_STREAM 256

#define BUMP_CODE 257 //Mã dánh khi nào thì tăng độ dài của từ mã

#define FLUSH_CODE 258

Trang 8

#define FIRST_CODE 259

#define UNUSED -1

struct dictionary { int code_value;

int parent_code;

char character;

} *dict[TABLE_BANKS];

#define DICT(i) dict[i>>8][i & 0xff]

char decode_stack[TABLE_SIZE];

unsigned int next_code;

int current_code_bits;

unsigned int next_bump_code;

void InitializeDictionary(void)

{

unsigned int i;

for(i=0;i<TABLE_SIZE;i++) DICT(i).code_value=UNUSED;

next_code=FIRST_CODE;

putc('F',stdout); //Báo hiệu khởi đầu hay khởi đầu lại từ điển.

current_code_bits=9; //Mã hiện tại.

next_bump_code=511;

}

/*===============================*/

void InitializeStorage(void) //Thủ tục cấp phát bộ nhớ.

{

int i;

for(i=0;i<TABLE_BANKS;i++)

{

dict[i]=(struct dictionary *) calloc(256,sizeof(struct dictionary));

if (dict[i]==NULL) fatal_error("Error allocating dictionary space");

}

}

/*==============================*/

void CompressFile(FILE *input,BIT_FILE *output,int argc,char *argv[])

{

int character;

int string_code;

unsigned int index;

InitializeStorage();

InitializeDictionary();

//ký tự đầu tiên được đọc vào.

Trang 9

if ((string_code=getc(input))==EOF) string_code=END_OF_STREAM;

while((character=getc(input))!=EOF) //Bắt đầu vòng lặp.

{

index=find_child_node(string_code,character);

if(DICT(index).code_value!=-1) string_code=DICT(index).code_value;

else

{

DICT(index).code_value=next_code++;

DICT(index).parent_code=string_code;

DICT(index).character=(char)character;

OutputBits(output,(unsigned long)string_code,current_code_bits);

string_code=character;

if(next_code >MAX_CODE)

{

OutputBits(output,(unsignedlong)FLUSH_CODE,current_code_bits);

InitializeDictionary();

}

else if (next_code > next_bump_code) {

OutputBits(output,(unsigned long)BUMP_CODE,current_code_bits);

current_code_bits++;

next_bump_code <<= 1;

next_bump_code |= 1;

putc('B',stdout);

}

}

OutputBits(output,(unsigned long)string_code,current_code_bits);

OutputBits(output,(unsigned long)END_OF_STREAM,current_code_bits);

while (argc >0)

printf("Unknown argument :%s\n",*argv++);

}

/*======================*/

void ExpandFile(BIT_FILE *input,FILE *output,int argc,char *argv[])

{

int new_code;

int old_code;

int character;

unsigned int count;

InitializeStorage();

while (argc >0)

printf("Unknown argument:%s",*argv);

for(;;)

10

Tạo thêm 1 đỉnh ứng với phrase mới trong từ điển

và gán các giá trị cần thiết.

Ghi

string_co

de vừa

tìm được

ra tệp và

bắt đầu

việc tìm

mới Phần xử

lý khởi

đầu lại

từ điển.

Phần

thay đổi

độ dài từ

Thực

hiện

một số

lệnh

Trang 10

InitializeDictionary();

old_code=(unsigned int)InputBits(input,current_code_bits);

if (old_code==END_OF_STREAM) return;

character=old_code;

putc(old_code,output);

for(;;)

{

new_code=(unsigned int)InputBits(input,current_code_bits);

if (new_code==END_OF_STREAM) return;

//Nếu (new_code==END_OF_STREAM) thì gọi intializeDictionnary() để gán lại từ điển.

if (new_code==FLUSH_CODE) break;

if (new_code==BUMP_CODE)

//Nếu (new_code== BUMP_CODE ) thìđộ dài từ mã được tăng bằng lệnh: current_code_bits++

{

current_code_bits++;

putc('B',stdout);

continue;

} //Nếu gặp phải mã chưa xuất hiện trong từ điển thì xử lý như sau:

if (new_code >= next_code)

{

decode_stack[0]=(char)character;

count=decode_string(1,old_code);

}

else count=decode_string(0,new_code);

character=decode_stack[count-1];

while(count>0) putc(decode_stack[ count],output);

DICT(next_code).parent_code=old_code;

DICT(next_code).character=(char)character;

next_code++;

old_code=new_code;

}

}

}

/*==============================*/

unsigned int find_child_node(int parent_code,int child_character)

{

unsigned int index;

int offset;

index=(child_character << (BITS-8)) ^ parent_code;

if (index==0) offset=1;

else offset=TABLE_SIZE-index;

for (;;)

{

//Trong trường hợp còn lại chúng ta gọi code_string() để xử lý.

Trang 11

if (DICT(index).code_value==UNUSED) return((unsigned int)index);

if (DICT(index).parent_code==parent_code &&

DICT(index).character==(char)child_character)

return(index);

if (index >= offset) index-=offset;

else index+=TABLE_SIZE-offset;

}

}

/*=========================================*/

unsigned int decode_string(unsigned int count,unsigned int code)

{

while(code>255)

decode_stack[count++]=DICT(code).character;

code=DICT(code).parent_code;

}

decode_stack[count++]=(char)code;

return(count);

}

/*===================*/

Chúng ta tăng kích thước của từ điển lên tới 15 bit Nhưng không phải mọi code đều dùng15 bit để mã Chúng ta sử dụng 9 bit cho 512 phrase đầu (từ 0 đến 511), 10 bit cho các phrase từ 512 đến 1023, và cứ thế tiếp tục Như vậy cần thêm một ký tự đặc biệt là BUMP_CODE để đánh dấu khi nào thì tăng độ dài của từ mã Biến

unsigned int next_bump_code được sử dụng để theo dõi việc này Độ dài từ code ở biến current_code_bits.

Một cải tiến thứ hai chúng ta đưa vào để xử lý khi từ điển đã lớn hết cỡ Trong trường hợp này chúng ta cho ra một ký tự đặc biệt là FLUSH_CODE Khi đó sẽ xây dựng từ điển lại từ đầu

Trong chương trình này, chúng ta chia từ điển ra TABLE_BANKS khoảng, mỗi khoảng có 256 phrase và sử dụng macro

#define DICT(i) dict[i>>8][i & 0xff]

để xử lý index

Thủ tục void InitializeDictionary(void) gồm các lệnh:

for(i=0;i<TABLE_SIZE;i++) DICT(i).code_value=UNUSED;

next_code=FIRST_CODE;

putc('F',stdout); // báo hiệu khởi đầu hay khởi đầu lại từ điển

current_code_bits=9;

//Thủ tục trả lại số ký tự của phrase.

Ngày đăng: 25/07/2013, 21:21

HÌNH ẢNH LIÊN QUAN

Sơ đồ phụ thuộc của các thủ tục trong LZW15 - Thuật toán nén LZW
Sơ đồ ph ụ thuộc của các thủ tục trong LZW15 (Trang 13)

TỪ KHÓA LIÊN QUAN

w