Giả sử ta muốn truyền một đoạn tin nhắn có nội dung là “aaaabbbbcccc” bằng mã Ascii thì chúng ta cần 8 bit cho một ký tự, như vậy cần đến 96 bit để mã hóa được đoạn tin nhắn trên.. Vì vậ
Trang 1TRƯỜNG ĐẠI HỌC BÁCH KHOA HÀ NỘI
VIỆN ĐIỆN TỬ - VIỄN THÔNG
BÁO CÁO BÀI TẬP LỚN
Lý thuyết thông tin
Giảng viên hướng dẫn : Thầy Nguyễn Hữu Phát Nhóm sinh viên thực hiện : Phan Việt Nam – 20193036
Trần Đức Tin – 20193134
Tháng 1/2022
Trang 2I GIỚI THIỆU CHUNG VỀ ĐỀ TÀI
Trong Lý thuyết thông tin, mã thống kê tối ưu được thực hiện tại nguồn nhằm giải quyết vấn đề thứ nhất của hệ thống truyền tin đó là tốc độ truyền tin Giả sử ta muốn truyền một đoạn tin nhắn có nội dung là “aaaabbbbcccc” bằng mã Ascii thì chúng ta cần 8 bit cho một
ký tự, như vậy cần đến 96 bit để mã hóa được đoạn tin nhắn trên Vì vậy chúng ta cần tìm ra một phương pháp mã hóa nhằm tăng tốc độ truyền tin, để làm được điều đó, người ta làm giảm chiều dài trung bình của từ mã bằng cách các từ mã có xác suất nhỏ thì có chiều dài lớn và từ mã có xác suất lớn thì chiều dài nhỏ
Kết hợp quá trình học tập môn Lý thuyết thông tin, nhóm chúng
em nhận thấy hai phương pháp mã hóa theo Shannon và Huffman có tính dụng cao, nó giúp mã hóa thông tin, nén dữ liệu dựa trên tần suất xuất hiện của các ký tự cần mã hóa sao cho dung lượng sau khi mã hóa được giảm bớt nhằm tối ưu mục đích lưu trữ Vì vậy, chúng em quyết định lựa chọn 2 phương pháp mã hóa này làm chủ đề cho bài tập lớn
II NỘI DUNG ĐỀ TÀI
1 Các định lý và thông số của mã thống kê
Định lý 1: Trong hệ nhị phân, entropy của nguồn luôn nhỏ hơn
hoặc bằng chiều dài trung bình của từ mã:
Định lý 2: Trong hệ nhị phân, người ta luôn tìm được bộ mã thỏa mãn tính chất tối ưu với chiều dài trung bình nằm trong khoảng []
Để đánh giá tính hiệu quả của mã thống kê, người ta đưa ra thông số
hệ số nén thông qua tỷ lệ:
Trang 32 Bài toán: Cho nguồn tin X = {x , x , x , x , x ,…} với xác suất 1 2 3 4 5
tương ứng là P(X)={p(x ), p(x ), p(x ), p(x ), p(x ), …} Thực hiện mã1 2 3 4 5
hóa nguồn tin trên
2.1 Mã hóa theo Shannon
a Thuật toán
Bước 1: Sắp xếp các nguồn tin theo thứ tự xác suất giảm dần
Bước 2: Thay xác suất p(xi)=Pi
Trong đó
Bước 3: Chuyển P từ dạng thập phân sang nhị phân tương ứngi
Bước 4: Xác định chiều dài l : H(x ) l H(x ) + 1i i i i
Bước 5: Lấy phần sau dấu phảy có chiều dài bằng li
b Code
Các khối trong code bao gồm:
Khối nhập thông tin đầu vào: 2 phương thức nhập
+ Nhập theo xác suất + Nhập theo tần số
void Source ( float x[], int n)
{
cout<< " Moi ban chon phuong thuc nhap: " <<endl;
cout<< " 1 Nhap duoi dang xac suat: " <<endl;
cout<< " 2 Nhap duoi dang tan so: " <<endl;
int c;
check_Number(c);
if (c == 1
{
cout<< " Moi ban nhap xac suat: " <<endl;
for ( int i = ; i <n; i ++ )
{
do
{
cout<< " x[ " << i<< " ]: " ;
check_Float(x[i]);
}
Trang 4while (x[i] <= 0 ||x[i] >= 1 );
}
}
if (c != 1
{
float sum1 = ;
cout<< " Moi ban nhap tan so: " <<endl;
for ( int i = ; i <n; i ++ )
{
cout<< " x[ " << i<< " ]: " ;
check_Float(x[i]);
}
for ( int i = ; i <n; i ++ )
{
sum1 +=x[i];
}
for ( int i = ; i <n; i ++ )
{
x[i] =x[i] / sum1;
}
}
}
Khối kiểm tra điều kiện: Dữ liệu nhập vào có thỏa mãn hay không bao gồm các hàm: check_Number(), check_Float(), check()
- Hàm check_Number(): Đảm bảo số nguồn đầu vào phải là số
nguyên:
void check_Number( int n)
{
int check = scanf( " %d " , &n);
//Nhap lai neu la chu hoac so < 1
while (check != 1 || n< )
{
if (check != ){
cout<< " Khong phai so! " <<endl;
} else {
cout<< " So nhap phai >= 1 " <<endl;
}
fflush(stdin);
cout<< " Nhap lai: "
check = scanf ( " %d " &n);
}
};
Trang 5- Hàm check_Float(): Đảm bảo các xác suất nhập vào phải ở dạng
số thực:
void check_Float ( float& n)
{
int check = scanf( " %f " , &n);
while (check != )
{
cout<< " Nhap sai! " <<endl;
fflush(stdin);
cout<< " Nhap lai: "
check = scanf ( " %f " &n);
}
};
- Hàm Check(): Hàm này kiểm tra xem sau khi nhập xác suất vào thì tổng các xác suất đầu vào có bằng 1 hay không:
bool Check ( float x[], int n, float s)
{
s= ;
for ( int i = ; i <n; i ++ )
{
s+=x[i];
}
if (s== 1 ) return true ;
else return false ;
}
Khối sắp xếp và chuyển xác suất
Trong bài này, cụ thể ta sẽ sắp xếp theo thứ tự giảm dần:
// Hàm s p x p theo giá tr xác su t gi m d n ắ ế ị ấ ả ầ
void Sort( float x[], int n)
{
float temp;
for ( int i = 0 ; i < n - 1 ; i ++ )
{
for ( int j = i + 1 ; j < n; j ++ )
{
if (x[i] < x[j]){
Trang 6// Hoan vi 2 so a[i] va a[j]
temp = x[i];
x[i] = x[j];
x[j] = temp;
}
}
}
}
Tiếp theo, thay xác suất p(xi)=Pi , với P = :i
// Hàm tính xác su t c ng d n ấ ộ ồ
void Xacsuat ( float x[], float p[], int n)
{
p[ ] 0 = ;
for ( int i = ; i <n; i ++ )
{
p[i] =p[i - 1 +x[i - ];
}
}
Khối mã hóa
void Binary ( float l[], float l1[], float p[], float p1[], string str[],
int n)
{
for ( int i = ; i <n; i ++ )
{
p1[i] =p[i];
l1[i] =l[i];
while (l1[i] > )
{
if (p1[i] * >= 1
{
str[i] += " " 1 ;
p1[i] =p1[i] * - ;
}
else
{
p1[i] =p1[i] * ;
str[i] += " " 0 ;
}
l1[i] ;
}
}
}
Trong khối này, ta sẽ sử dụng mảng chuỗi str[] để lưu trữ giá trị sau khi mã hóa xác suất P , l[] và l1[] là 2 mảng để truyền vào độ dài từi
mã l còn 2 mảng p[] và p1[] là xác suất sau khi cộng dồn của cáci
Trang 7từ mã đó Chúng ta mã hóa bằng cách chuyển P từ cơ số 10 sangi
dạng nhị phân theo quy tắc lấy xác suất p1[i] x 2, nếu p1[i]x2 mà nhỏ hơn 1 thì bit đó bằng 0 Nếu p1[i]x2 ≥1 thì bit đó gán giá trị là
1 sau đó gán p1[i]= p1[i]x2-1 Tiếp tục thực hiện như trên cho đến khi chuỗi có độ dài bằng với l1[i] thì dừng lại, chuỗi thu được chính là từ mã cần mã hóa
Khối tính chiều dài trung bình
// Hàm tính chi u dài trung bình ề
float AverageLength( float x[], float l[], int n)
{
float sum1 = ;
for ( int i = ; i <n; i ++ )
{
sum1 +=x[i] *l[i];
}
return sum1;
}
Khối tính entropy
// Hàm tính entropy
float Entropy( float x[], int n)
{
float sum = ;
for ( int i = ; i <n; i ++ )
{
sum +=-x[i] * log(x[i]) / log( );
}
return sum;
}
c Kết quả: Như vậy với phương pháp mã hóa theo Shannon, đoạn tin nhắn “aaaabbbbcccc” nêu ra trong phần I sẽ chỉ cần 24 bit (so với 96 bit khi mã hóa bằng Ascii) để mã hóa
2.2 Mã hóa theo Huffman
a Thuật toán theo sơ đồ cây
Bước 1: Sắp xếp các nguồn tin theo thứ tự xác suất tăng dần hoặc giảm dần
Trang 8 Bước 2: Chọn hai tin có xác suất nhỏ nhất, gán 1 tin cho bit 0, tin còn lại cho bit 1
Bước 3: Cộng xác suất của 2 tin đó lại ta thu được tin mới và xác suất mới
Bước 4: Nếu chỉ còn lại 1 tin thì dừng lại, nếu không quay lại bước 2
Bước 5: Đọc từ mã từ dưới lên
b Code
Các khối trong code bao gồm:
Khối nhập thông tin đầu vào: 2 phương thức nhập
+ Nhập dữ liệu đầu vào
int n;
cout<< " So ky tu: " check_Number(n);
char s[ 100 ];
cout << " Nhap Ky tu: " <<endl ;
for ( int i = ;i < n;i ++ )
{
cin>>s[i];
if (check_character(s,i) == false ){
system( cls " );
cout<< " Da co ky tu trung! " <<endl;
cout<< " Ban da nhap: "
for ( int k = ;k < i;k ++ )
{
cout<<s[k];
}
cout<< " \n Moi Nhap tiep: " <<endl;
i ;
}
};
cout<< " Da nhap xong ky tu! " <<endl;
+ Nhập theo xác suất
label:
float xs[ 100 ],sum = ;
cout << " Nhap Xac Suat: " <<endl ;
Trang 9for ( int i = ;i < n;i ++ )
{
check_Float(xs[i]);
sum = sum xs[i]; +
}
if (sum != ){
cout<< " Ban nhap sai xac suat! " <<endl;
goto label;
}
XS insert end ( (XS), begin (xs), begin (xs) + n);
cout<< " Da nhap xong xac suat! " <<endl;
+ Nhập theo tần số
cout << " Nhap tan so: " <<endl ;
// sum: tong cac ky tu
float xs[ 100 ],sum;
for ( int i = ;i < n;i ++ )
{
check_Number(Tanso[i]);
sum sum Tanso[i] = + * 1.0 ;
}
for ( int i = ;i < n;i ++ )
{
xs[i] = float ((Tanso[i] * 1.0 ) (sum * 1.0 ));
}
XS insert end ( (XS), begin (xs), begin (xs) n); +
Khối kiểm tra điều kiện: Dữ liệu nhập vào có thỏa mãn hay không
void check_Number( int n)
{
int check = scanf( " %d " , &n);
//Nhap lai neu la chu hoac so < 1
while (check != 1 || n< )
{
if (check != ){
cout<< " Khong phai so! " <<endl;
} else {
cout<< " So nhap phai >= 1 " <<endl;
}
fflush(stdin);
cout<< " Nhap lai: "
check = scanf ( " %d " &n);
}
};
Trang 10Khối này kiểm tra nhằm đảm bảo số nguồn đầu vào phải là số nguyên dương
void check_Float ( float& n)
{
int check = scanf( " %f " , &n);
while (check != )
{
cout<< " Nhap sai! " <<endl;
fflush(stdin);
cout<< " Nhap lai: "
check = scanf ( " %f " &n);
}
};
Khối này đảm bảo xác suất nhập vào phải là số thực
bool check_character( char s[], int n)
{
for ( int i = ;i <n;i ++ )
{
for ( int j = + i 1 ;j < +n 1 ;j ++ )
{
if (s[i] ==s[j])
{
fflush(stdin);
return false ;
}
}
}
return true ;
}
Khối này đảm bảo sao cho các nguồn nhập vào không bị trùng
Khối tạo cây
void Tao_Cay ( vector < char > KyTu, vector < float > XSuat, vector <Li> L,
size_t size)
{
Node* left;
Node* right;
//
priority_queue <Node* , vector <Node*> , compare minHeap;
//
for (size_t i = 0 ; i < size; ++ i)
Trang 11{
minHeap.push(new Node(KyTu[ ], i XSuat[ ])); i
}
//
while (minHeap.size() != )
{
left = minHeap.top();
minHeap.pop();
right = minHeap.top();
minHeap.pop();
top = new Node( ' ' $ , left -> XacSuat right + -> XacSuat);
top -> left left; =
top -> right right; =
minHeap.push(top);
};
print_Code(minHeap.top(), "" ,L);
}
Cây nhị phân được tạo ra dựa trên hàng đợi ưu tiên
priority_queue với thứ tự ưu tiên được thể hiện trong struct compare:
struct compare
{
bool operator() (Node* l, Node* r)
{
return (l-> XacSuat > r-> XacSuat);
}
};
Theo quy tắc trên, ta sẽ tạo ra được một cây Huffman hoàn chỉnh và với mỗi node được tạo ra bằng cách cộng xác suất của
2 node có xác suất nhỏ nhất, ta sẽ gán cho node đó một ký hiệu
để thuận tiện cho việc đọc ($)
Khối mã hóa
void In_Ma (Node* root, string str, vector <Li> &L)
{
if (root == NULL)
return ;
if (root-> KyTu == ' ' $ )
Trang 12{
In_Ma(root-> left, str + " " 0 ,L);
In_Ma(root-> right, str + " " 1 ,L);
}
if (root-> KyTu != ' ' $ )
{
char s = root-> KyTu;
Li new_Li(s,str);
L.push_back(new_Li);
In_Ma(root-> left, str + " " 0 ,L);
In_Ma(root-> right, str + " " 1 ,L);
}
};
Khối này thực chất là khối đọc thông tin từ cây Huffman đã được tạo trước đó theo quy tắc, xét từ nút có giá trị xác suất bằng 1 từ cây Huffman, đi về phía tay trái thì chuỗi cộng thêm
0, đi về phía tay phải thì chuỗi cộng thêm 1 Từ đó ta sẽ thu được thông tin sau khi mã hóa của các nguồn đầu vào
Khối tính chiều dài trung bình và entropy
for ( int i = ;i < KyTu.size();i ++ )
{
H += ( float ) - XacSuat[ ] i * log(XacSuat[ ] / log( );
Ltb += ( float ) XacSuat[ ] i * L i [ ].L.length();
}
c Kết quả: Như vậy với phương pháp mã hóa theo Huffman, đoạn tin nhắn “aaaabbbbcccc” nêu ra trong phần I sẽ chỉ cần 20 bit (so với 96 bit khi mã hóa bằng Ascii) để mã hóa
III LỜI KẾT
Qua bài tập lớn Lý Thuyết Thông Tin này, chúng em đã hiểu và nắm rõ hơn kiến thức về 2 phương pháp mã hóa Shannon và Huffman cũng như củng cố và nhớ lại các kiến thức về ngôn ngữ lập trình C++ Lời cuối cùng, chúng xem xin chân thành cảm ơn thầy Nguyễn Hữu Phát đã giảng dạy và giúp đỡ chúng em nhiệt tình trong quá trình học tập Chúng em xin kính chúc thầy luôn mạnh khỏe và công tác tốt