Giải mã theo phương pháp số học

Một phần của tài liệu (LUẬN văn THẠC sĩ) các kỹ thuật kiểm thử đột biến và ứng dụng kiểm thử chương trình c (Trang 37)

2.4 Phép biến đổi Burrows-Wheeler (BWT)

2.4.1 Giới thiệu BWT

Phép biến đổi Burrow-Wheeler không phải là một thuật toán nén dữ liệu, mà chỉ làm thay đổi cấu trúc dữ liệu, tạo dữ liệu đầu vào cho các thuật toán nén dữ liệu khác như RLE, hoặc có thể kết hợp với phương pháp MTF để đạt được hiệu quả nén cao hơn với các thuật toán nén Entropy.

Mục đích của BWT là nhóm các ký tự giống nhau ở gần nhau hơn, khối ký tự mới nhận được khi nén sẽ hiệu quả hơn, để thực hiện được điều này ta lấy ví dụ trên chuỗi ký tự S như sau:

Cho chuỗi ký tự S = “NUNANUNONG”, để đơn giản ta không xét đến dấu cách.

2.4.2 Mã hóa

1) Tạo ra các khối dữ liệu bằng cách hốn vị vịng trịn ký tự cuối cùng của khối lên đầu khối. Ta thu được ma trận X[n×n], mỗi hàng của ma trận là một hoán vị của S: X 0 1 2 3 4 5 6 7 8 9 0 N U N A N U N O N G 1 G N U N A N U N O N 2 N G N U N A N U N O 3 O N G N U N A N U N 4 N O N G N U N A N U 5 U N O N G N U N A N 6 N U N O N G N U N A 7 A N U N O N G N U N 8 N A N U N O N G N U 9 U N A N U N O N G N 2) Sắp xếp các dịng ma trận theo thứ tự tăng dần, ta có X 0 1 2 3 4 5 6 7 8 9 0 A N U N O N G N U N 1 G N U N A N U N O N 2 N A N U N O N G N U 3 N G N U N A N U N O

4 N O N G N U N A N U 5 N U N A N U N O N G 6 N U N O N G N U N A 7 O N G N U N A N U N 8 U N A N U N O N G N 9 U N O N G N U N A N

3) Tạo ra khối dữ liệu mới L là cột cuối cùng của ma trận. Giá trị D lưu trữ vị

trí ký tự đầu tiên của S, D là nguồn gốc để khôi phục L thành S

X F L 0 A N U N O N G N U N 1 G N U N A N U N O N 2 N A N U N O N G N U 3 N G N U N A N U N O 4 N O N G N U N A N U 5 N U N A N U N O N G 6 N U N O N G N U N A 7 O N G N U N A N U N 8 U N A N U N O N G N D 9 U N O N G N U N A N Nhận xét:

- L có thể được lưu trữ trong mảng một chiều:

0 1 2 3 4 5 6 7 8 9

L= N N U O U G A N N N

- Tương tự với F cũng là mảng một chiều nhận giá trị là cột đầu tiên của ma trận,

F được suy ra từ mảng L bằng cách sắp xếp L tăng dần:

0 1 2 3 4 5 6 7 8 9

F= A G N N N N N O U U

void bwt_encode ( FILE *in, FILE *out )

{ unsigned int i = 0, last_index = BWT_SIZE; while( 1 ) {

/* get bytes and save to buffer. */

nread=fread((unsigned char *)bwt_buf, 1, BWT_SIZE, in); if ( nread == 0 ) break;

/* write the size of the block read. */

fwrite( &nread, sizeof( unsigned int ), 1, out ); /* initialize index table. */

for ( i = 0; i < nread; i++ ) { index_table[i] = i;

}

/* sort the bwt buffer array; */

qsort( index_table, nread, sizeof(int),

(int (*)(const void *, const void *))bwt_comp); /* output Last column buffer. */

last_index = nread;

for ( i = 0; i < nread; i++ ){ if ( index_table[i] == 0 ){

putc( bwt_buf[ nread-1 ], out ); last_index = i;

}

else putc( bwt_buf[ index_table[i] - 1], out ); }

fprintf(stderr,"\n-[last index: %5d]- ", last_index); /* output index of the original string in Last. */ fwrite( &last_index, sizeof(unsigned int), 1, out ); }

}

int bwt_comp( unsigned int *a, unsigned int *b ) { register int a1 = *a, b1 = *b, tmp = a1;

if ( bwt_buf[a1] != bwt_buf[b1] ) {

if ( bwt_buf[a1] > bwt_buf[b1] ) return 1; else return -1;

} else {

!= bwt_buf[++b1 == nread? b1 = 0 : b1] ){ if (bwt_buf[a1] > bwt_buf[b1]) return 1; else return -1; } } while ( a1 != tmp ); } return 0; } 2.4.3 Giải mã

Mã hóa bằng BWT cho ta mảng L và giá trị D. Từ L ta có thể suy ra được F. Để có thể suy từ L ra S, ta phải tìm mối quan hệ giữa L và F. Với mỗi ký tự trong L ta đều suy ra ký tự tiếp theo của S từ F căn cứ vào chỉ số, bởi vì quá trình chuyển

đổi BWT là quá trình hốn vị vịng quanh S.

Giả sử ta biết ký tự si trong L có vị trí là k (si = L[k]), ta cũng biết ký tự tiếp theo trong S là si+1 = F[k]. Ta đã biết được si+1 thì cũng biết được si+2, tương tự như vậy ta sẽ suy ra được S. Hình 11 minh họa quá trình này.

Hình 11. Minh họa cách giải mã Burrows - Wheeler

Giá trị D cho ta biết ký tự đầu tiên của S trong L và ký tự tiếp theo của S trong F. Như vậy ta cần một mảng phụ để lưu trữ chỉ số của từng ký tự trong L khi sắp xếp. Ta định nghĩa mảng T để lưu trữ (Transformation vector).

F L

s3 s2

s2 s1

Minh họa ví dụ

L ban đầu L sau khi sắp xếp

0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9

L= N N U O U G A N N N F= A G N N N N N O U U

T= 6 5 0 1 7 8 9 3 2 4 D

Hình 12 suy ra được S

Hình 12. Minh họa cách giải mã ví dụ

Bắt đầu từ ký tự đầu tiên có chỉ số D=8; s1=L[8] = “N”; Ký tự tiếp theo s2=F[8] = “U” = L[T[8]] = L[2];

Ký tự tiếp theo s3 = F[2] = “N” = L[T[2]] = L[0]; Ký tự tiếp theo s4 = F[0] = “A” = L[T[0]] = L[6];

Q trình cứ tiếp tục cho đến khi ta có T[5] = D thì kết thúc, kết quả ta thu được là chuỗi “NUNANUNONG”.

void bwt_decode ( FILE *in, FILE *out ) { unsigned int i, findex, last_index;

unsigned int sum, temp; while ( 1 ) {

/* get the size of the block. */ fread ( &nread, sizeof(int), 1, in );

F Index L T A 0 N 6 G 1 N 5 N 2 U 0 N 3 O 1 N 4 U 7 N 5 G 8 N 6 A 9 O 7 N 3 U 8 N 2 ←D U 9 N 4

/* get character input (the last column). */ nread = fread( bwt_buf, 1, nread, in ); if ( nread == 0 ) break;

/* get the last index "pointer." */

fread ( &last_index, sizeof(int), 1, in ); /* initialize frequency array. */

for ( i = 0; i < 256; i++ ) { freq[i] = 0;

}

/* ---- count frequency of characters. ---- */ for ( i = 0; i < nread; i++ ) {

freq[ bwt_buf[i] ]++; }

/* ---- cumulative sum. ---- */ sum = 0;

/* after the index_table is filled in,

the freq[] array will contain the cumulative sum for all bytes. */ for ( i = 0; i < 256; i++ ) {

if ( freq[i] ) {/* only if existent in the last column! */ temp = freq[i]; /* save. */

freq[i] = sum; /* the cumulative sum.*/ sum += temp; /*add the current byte's frequency.*/ }

}

/* ---- finally, fill in the index_table. ---- */ for ( i = 0; i < nread; i++ ) {

index_table[ freq[bwt_buf[i]]++ ] = i; }

/*NOTE: last_index is the row index of the original string in the *sorted* matrix (see gtbwt5.c).*/

findex = index_table[last_index];

/* output the bytes; reverse BWT. */ while ( nread-- ){

putc( bwt_buf[ findex ], out ); findex = index_table[ findex ]; }

} }

2.5 Phép biến đổi Move-To-Front (MTF)

2.5.1 Giới thiệu MTF

Ký hiệu S là khối dữ liệu nguồn cần xử lý; Si là các ký tự trong S. D là khối đích chứa mã các ký tự đầu ra của MTF.

MTF là phương pháp dịch chuyển các ký hiệu về vị trí đầu tiên trong khối cần mã. Điều này làm cho các ký hiệu xuất hiện liên tiếp sẽ có mã là 0. Kết quả là khối mã hóa có rất nhiều ký hiệu có mã là 0.

Để đơn giản, ta xét các ký hiệu trong bảng mã ASCII, được lưu trữ theo thứ tự từ 0 đến 255 trong một mảng ký tự. Mỗi lần đọc ký tự từ khối cần nén, ta di chuyển ký tự đó trong mảng về vị trí đầu tiên (vị trí 0).

2.5.2 Mã hóa

1) Đọc ký tự ch từ khối dữ liệu nguồn S

2) Ghi ký tự là vị trí xuất hiện ch trong bảng mã vào khối dữ liệu đích D 3) Di chuyển ký tự ch lên đầu bảng mã

4) Lặp lại các bước trên cho đến khi hết khối dữ liệu nguồn. Minh họa phương pháp MTF qua ví dụ sau:

- Khối dữ liệu cần mã hóa gồm các ký tự S = “NNUOUGANNN”

Đọc từ Si Ghi vào D Bảng mã 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 A B C D E F G H I J K L M N O P Q R S T U V W X Y Z N 13 N A B C D E F G H I J K L M O P Q R S T U V W X Y Z N 0 N A B C D E F G H I J K L M O P Q R S T U V W X Y Z U 20 U N A B C D E F G H I J K L M O P Q R S T V W X Y Z O 15 O U N A B C D E F G H I J K L M P Q R S T V W X Y Z U 1 U O N A B C D E F G H I J K L M P Q R S T V W X Y Z G 9 G U O N A B C D E F H I J K L M P Q R S T V W X Y Z A 4 A G U O N B C D E F H I J K L M P Q R S T V W X Y Z N 4 N A G U O B C D E F H I J K L M P Q R S T V W X Y Z N 0 N A G U O B C D E F H I J K L M P Q R S T V W X Y Z N 0 N A G U O B C D E F H I J K L M P Q R S T V W X Y Z Bảng 7. Mơ tả q trình mã hóa MTF

Như vậy, sau khi dùng phương pháp MTF với khối S = “NNUOUGANNN” ta có khối kết quả D = “13 0 20 15 1 9 4 4 0 0”.

Sau đây là đoạn mã MTF, trong đó head là con trỏ trỏ vào đầu bảng mã /* find c. */ p = head; while( p->c != c ) { i++; p = p->next; } /* move-to-front. */ if ( p->prev ) { if ( p->next ) { p->prev->next = p->next; p->next->prev = p->prev; } else { p->prev->next = NULL; tail = p->prev; }

p->prev = NULL; p->next = head; head->prev = p; head = p; } /* front, don't MTF! */ return i; 2.5.3 Giải mã

Quá trình giải mã MTF ta cũng thực hiện tương tự như q trình mã hóa: 1) Đọc ký tự ch từ nguồn

2) Ghi ký tự ch trong bảng mã vào khối đích 3) Di chuyển ký tự ch lên đầu bảng mã

4) Lặp lại các bước trên cho đến khi hết khối dữ liệu nguồn

- Khối dữ liệu cần giải mã gồm các ký tự S = “13 0 20 15 1 9 4 4 0 0” - Bảng ký tự gồm 26 chữ cái “ABCDEFGHIJKLMNOPQRSTUVWXYZ” Đọc từ Si Ghi vào D Bảng mã 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 A B C D E F G H I J K L M N O P Q R S T U V W X Y Z 13 N N A B C D E F G H I J K L M O P Q R S T U V W X Y Z 0 N N A B C D E F G H I J K L M O P Q R S T U V W X Y Z 20 U U N A B C D E F G H I J K L M O P Q R S T V W X Y Z 15 O O U N A B C D E F G H I J K L M P Q R S T V W X Y Z 1 U U O N A B C D E F G H I J K L M P Q R S T V W X Y Z 9 G G U O N A B C D E F H I J K L M P Q R S T V W X Y Z 4 A A G U O N B C D E F H I J K L M P Q R S T V W X Y Z 4 N N A G U O B C D E F H I J K L M P Q R S T V W X Y Z 0 N N A G U O B C D E F H I J K L M P Q R S T V W X Y Z 0 N N A G U O B C D E F H I J K L M P Q R S T V W X Y Z Bảng 8. Mơ tả q trình giải mã MTF

p = head; /* find c. */ while( i-- ) { p = p->next; } if ( p->prev ) { /* move-to-front. */ if ( p->next ) { p->prev->next = p->next; p->next->prev = p->prev; } else { p->prev->next = NULL; tail = p->prev; } p->prev = NULL; p->next = head; head->prev = p; head = p; } return p->c; 2.6. Phân tích phương pháp MTF 2.6.1. Tổ chức dữ liệu

Đối với bảng mã, trong phạm vi luận văn này ta xét với 256 ký hiệu trong bảng mã ASCII, có thể tổ chức dưới dạng danh sách đặc hoặc danh sách liên kết. Chọn cách tổ chức nào phù hợp và tăng tốc độ xử lý là yêu cầu chính. Trong luận văn này tác giả chọn cách tổ chức dưới dạng danh sách liên kết, vì một số lý do sau:

- Sau mỗi lần tìm kiếm được một ký hiệu trong bảng mã, ta cần phải di chuyển ký hiệu đó lên đầu bảng mã. Trong danh sách liên kết ta thực hiện điều này dễ dàng và nhanh hơn, bởi vì ta chỉ cần thay đổi liên kết các ký hiệu. Còn trong danh sách đặc ta phải di chuyển n ký hiệu (với n là vị trí tìm được ký hiệu).

- Trong cả danh sách liên kết và danh sách đặc, nếu áp dụng thuật tốn tìm kiếm ký hiệu bằng phương pháp tìm kiếm nhị phân sẽ rất khơng có lợi, bởi lẽ sau mỗi lần tìm thấy ta sẽ thay đổi vị trí ký hiệu lên đầu tiên trong bảng ký hiệu. Hơn nữa, với các ký hiệu giống nhau liên tiếp (kết quả của phương pháp biến đổi BWT) thì ký hiệu cần tìm tiếp theo thường đứng ở đầu danh sách, nên phù hợp hơn với phương pháp tìm kiếm tuần tự.

2.6.2. Thực hiện

Việc tìm kiếm ký hiệu bằng phương pháp tìm kiếm tuần tự, nên chi phí cho mỗi lần tìm ký hiệu là O(n), gọi kích thước trong mỗi khối dữ liệu cần biến đổi là k, nên chi phí tồn bộ thuật tốn là O(nk). Với tính chất dồn các ký hiệu giống nhau của phương pháp BWT, nên việc tìm kiếm ký hiệu thứ hai có thời gian ít hơn vì nó thường xuất hiện ở đầu bảng mã. Chi phí cho tồn bộ thuật tốn là O(nk) là khơng đáng kể, nhưng hiệu quả của MTF đem lại rất cao.

2.7 Lược đồ nén dữ liệu ứng dụng phương pháp MTF

2.7.1 Lược đồ nén dữ liệu

Một lược đồ điển hình của thuật toán nén dữ liệu Burrows - Wheeler được trình bày như hình 13 bao gồm 4 giai đoạn. Mỗi giai đoạn sẽ xử lý và biến đổi khối dữ liệu để tạo dữ liệu đầu vào cho giai đoạn tiếp theo. Khi nén dữ liệu thì các giai đoạn xử lý lần lượt từ trái sang phải; khi giải nén thì thực hiện từ phải sang trái.

Hình 13. Lược đồ nén dữ liệu của Burrows-Wheeler

Giai đoạn đầu tiên của thuật toán nén là phép biến đổi BWT. Mục đích của giai đoạn này là sắp xếp dữ liệu theo cách mà các ký tự có cùng ngữ cảnh nhóm lại gần nhau hơn. BWT khơng làm thay đổi số lượng các ký hiệu có trong khối dữ liệu, trừ khi ta phải thêm vào chỉ số của ký hiệu đầu tiên của khối dữ liệu chưa mã hóa,

input

ký hiệu này được sử dụng để biến đổi khối dữ liệu về thứ tự ban đầu. Chỉ số này là một số nguyên có giá trị từ 0 đến n-1 (n là số lượng ký tự trong khối dữ liệu nguồn), và nó được đặt tại vị trí đầu tiên của khối dữ liệu được mã hóa bằng BWT.

Giai đoạn thứ hai là phương pháp dịch chuyển MTF, được sử dụng trong thuật toán nén dữ liệu BWCA. MTF là một thuật toán cập nhật lại khối dữ liệu bằng cách thay thế các ký hiệu lặp lại thường xuyên bằng các ký hiệu có số thứ tự là 0. MTF cũng khơng làm thay đổi kích thước khối dữ liệu. Điều kỳ diệu ở đây là trước giai đoạn này là phương pháp BWT đã làm biến đổi vị trí các ký hiệu, các ký hiệu giống nhau đứng cạnh nhau, vì thế khi di chuyển ký hiệu đứng đầu về phía trước thì ký hiệu ngay sau đó sẽ có mã là “0”.

Giai đoạn thứ ba là nén số lượng các ký hiệu sử dụng thuật toán RLE. Thuật toán RLE sẽ dồn các ký hiệu lặp đi lặp lại nhiều lần bằng cách thay thế số lần lặp của ký hiệu đó. Như vậy thuật tốn này thu nhỏ kích thước của khối dữ liệu nhưng lại làm tăng số ký hiệu trong khối dữ liệu bởi vì RLE cịn phải lưu giữ số lần lặp của ký hiệu đó, số lần lặp là một số nguyên cũng chính là mã của ký hiệu mới có giá trị từ 0 đến 255, nếu số lần lặp lớn hơn 255 thì ta phải dồn thành hai đoạn.

Giai đoạn thứ tư là phương pháp nén dữ liệu Entropy. Entropy Coding là một phương pháp nén dữ liệu khơng mất thơng tin, nó phụ thuộc vào tần suất xuất hiện các ký hiệu. Trong các phương pháp mã hóa Entropy có các thuật toán Huffman Coding và Arithmetic Coding.

2.7.2 Nén dữ liệu

Để làm rõ các giai đoạn nén dữ liệu khác nhau, hình 14 hiển thị các dịch chuyển khối dữ liệu đầu vào là "abracadabraabracadabra" bằng mã Hexa. Khối dữ liệu trong các bài tốn thực tế là 64KB, trong ví dụ này ta bỏ qua chỉ số của ký hiệu đầu tiên trong phép dịch chuyển BWT.

BWT input: 61 62 72 61 63 61 64 61 62 72 61 61 62 72 61 63 61 64 61 62 72 61 BWT output: 61 72 72 64 64 61 72 72 63 63 61 61 61 61 61 61 61 61 62 62 62 62 MTF output: 61 72 00 65 00 02 02 00 65 00 02 00 00 00 00 00 00 00 65 00 00 00 RLE output: 61 72 00 65 00 02 02 00 00 65 00 02 00 00 05 65 00 00 01

Huffman: 89 67 EC E5 62 03

Hình 14. Thể hiện các bước nén khối dữ liệu "abracadabraabracadabra".

Ta nhận thấy trong giai đoạn BWT kết xuất khối dữ liệu ra có các ký hiệu cùng ngữ cảnh được lặp đi lặp lại liên tiếp nhau. Giai đoạn MTF tạo ra các ký hiệu có mã 00 nhiều hơn so với khối dữ liệu ban đầu. Giai đoạn thứ ba là RLE dồn các ký hiệu giống nhau lại với nhau và ghi lại số lần lặp của ký hiệu đó. Trong ví dụ trên ta thấy có 7 ký hiệu 00 được nén lại thành “00 00 05”, nghĩa là có 5 ký hiệu đứng sau ký hiệu 00. Giai đoạn thứ 4 là thuật toán Huffman xử lý trên chuỗi BIT theo cấu trúc cây Huffman. Hai giai đoạn BWT và MTF không làm thay đổi kích

Một phần của tài liệu (LUẬN văn THẠC sĩ) các kỹ thuật kiểm thử đột biến và ứng dụng kiểm thử chương trình c (Trang 37)

Tải bản đầy đủ (PDF)

(63 trang)