Thuật toán nén ban đầu được phát triển dùng để giảm bớt lưu trữ dữ liệu trên các đĩa cứng.. Ý nghĩa khoa học và thực tiễn của đề tài: Thuật toán nén hiện nay còn có thể áp dụng vào nhiều
Ý nghĩa khoa học và thực tiễn của đề tài
Thuật toán nén hiện nay có thể áp dụng vào nhiều vấn đề mới, đặc biệt là trong quản lý dữ liệu Big Data Việc xử lý lượng lớn dữ liệu và tối ưu hóa chi phí lưu trữ là lúc thuật toán nén phát huy tiềm năng Tôi quyết định chọn đề tài này để nghiên cứu và ứng dụng các thuật toán, nhằm hiểu rõ hơn về ý tưởng và ứng dụng của chúng trong các hoạt động nghiên cứu sau này.
THIỆU
Đặt vấn đề và tính cấp thiết
Nhu cầu lưu trữ và truyền tải dữ liệu, bao gồm văn bản, hình ảnh, audio và video, đang gia tăng mạnh mẽ Việc giảm dung lượng dữ liệu mà vẫn giữ nguyên thông tin gốc là rất quan trọng để tiết kiệm băng thông, rút ngắn thời gian truyền tải và giảm chi phí lưu trữ.
Mục tiêu nghiên cứu
+,Phân tích nguyên lý entropy làm cơ sở cho thuật toán Huffman coding. +,Hiện thực và đánh giá hiệu năng thuật toán Huffman coding.
+,Hiện thực và đánh giá hiệu năng thuật toán LZ77.
+,So sánh trực tiếp tỉ lệ nén, throughput và độ phức tạp tính toán giữaHuffman và LZ77.
Phạm vi và giới hạn đề tài
+,Phạm vi nghiên cứu: tập trung vào hai thuật toán lossless là Huffman coding và LZ77.
+,Dữ liệu thử nghiệm: văn bản tiếng Việt có dấu, file ảnh PNG/BMP và file audio WAV chất lượng CD.
Bài viết này giới hạn trong việc khảo sát các thuật toán nén Huffman và LZ77, không đề cập đến các thuật toán khác như Arithmetic, Range coding, LZ78, LZMA, và các phương pháp nén mất mát Ngoài ra, bài viết cũng không tối ưu phần cứng và không nghiên cứu về nén video phức tạp.
CƠ SỞ LÝ THUYẾT
Lý thuyết mã hóa Entropy
Mã Entropy sử dụng cơ chế dự đoán ký hiệu để xác định phân phối xác suất cho ký hiệu tiếp theo có thể được mã hóa Bộ ký hiệu có thể xuất hiện được gọi là bảng chữ cái, và phân phối xác suất cung cấp ước lượng xác suất cho mỗi ký hiệu trong bảng chữ cái.
Số lượng bit biểu diễn ký hiệu được gọi là s, có vai trò chứa nội dung thông tin của ký tự Nội dung thông tin I(s) liên quan đến xác suất dự đoán Pr[s] và được tính bằng hàm I(s) = -log Pr[s] bit Lượng trung bình thông tin với một ký hiệu trên toàn bộ bảng chữ cái được gọi là Entropy của phân phối xác suất, ký hiệu là H.
H = ∑ Pr[𝑠] 𝑠 * I(s) = ∑ − Pr[𝑠] ∗𝑙𝑜𝑔𝑃𝑟[𝑠] 𝑠, trong đó H được mô tả là hướng làm giảm số lượng trong một mol không khí Đây là lý thuyết thuộc về vật lý do nhà vật lý học Ludwig Boltzmann đưa ra vào năm 1872, được gọi là định lý H (H-theorem).
Sau đó nhà toán học Claude Shannon đã phát triển trên lý thuyết này ra công thức trên trong mã hóa dữ liệu vào năm 1948
Mã Entropy có một phương pháp thực thi khác là mã tiền tố, trong đó mỗi ký tự được thay thế bằng một xâu nhị phân tương ứng Trong bộ mã ASCII mở rộng, 256 ký hiệu được mã hóa bằng xâu nhị phân 8 bit từ 0 đến 255 Tuy nhiên, trong một đoạn văn bản, chỉ có hai loại ký tự xuất hiện với tần số khác nhau; một loại có tần số cao hơn và loại còn lại có tần số thấp Việc giữ nguyên mã hóa 8 bit cho cả hai ký hiệu sẽ dẫn đến lãng phí bộ nhớ Để khắc phục, cần giảm số bit cho các ký hiệu xuất hiện nhiều lần, nhưng điều này có thể gây nhầm lẫn với các ký hiệu khác.
Mã tiền tố là một khái niệm quan trọng trong việc giải quyết vấn đề, được định nghĩa là một tập hợp các ký hiệu mà không có ký hiệu nào là tiền tố của ký hiệu khác trong cùng tập hợp.
Ví dụ: Mã hóa chuỗi là “ARRAY” Chuỗi có 3 loại ký hiệu là ‘A’, ‘R’, ‘Y’
Mã hóa với 8 bit chuỗi ARRAY:
Mã hóa chuỗi với 2 bit cho mỗi ký tự (loại bỏ các ký hiệu không xuất hiện) nhằm lược bớt các số bit không thật sự cần thiết:
Mã hóa chuỗi lược bớt số 0 để giảm số lượng bit (đây chưa phải là mã tiền tố vì 01 có chứa tiền tố 0, mà 0 là được mã hóa thành ‘A’):
Mã hóa chuỗi với mã tiền tố (‘R’ sẽ được mã hóa thành 10, không chứa tiền tố 0 của ký hiệu ‘A’ và ‘Y’ được đổi thành 11)
Chuỗi thông thường chưa nén gồm 5 ký hiệu: 5 * 8 = 40 bit
Chuỗi sau khi thực hiện mã hóa với mã tiền tố là: 8 bit Phương pháp mã hóa Entropy với chuỗi là “ARRAY”, ta có tỉ lệ (ratio) nén là:
Kí c h t hướ c d ữ li ệ u nguy ê n t hủ y = 1 – 40 8 = 0.8 ↔ 80%
Một loại mã hóa khác thuộc lớp Entropy là mã hóa số học (Arithmetic
Phương pháp Coding được phát triển bởi Peter Alias vào đầu những năm 1960, chủ yếu dựa vào xác suất xảy ra trong một khoảng nhất định, bao gồm xác suất cận dưới và xác suất cận trên.
Công thức tính cận trên: cận dưới cũ + khoảng + cận trên(x).
Công thức tính cận dưới: cận trên cũ + khoảng + cận dưới(x)
Khoảng được xác định bằng cách lấy cận trên cũ trừ đi cận dưới cũ, và cận trên(x) cùng cận dưới(x) để xác định giới hạn cho ký tự x Giả sử chuỗi là: “SWISS_MISS”.
Ký tự Tính toán cận
Bảng 1:Tính toán để mã hóa trong mã hóa toán học
2.3 Lý thuyết nén LZ77(Thuật toán cửa sổ trượt):
Thuật toán LZ77 (còn được gọi là LZ1) do hai nhà khoa học Abraham
LZ77, được phát triển bởi Lempel và Jacob Ziv vào năm 1977, là thuật toán khởi đầu cho nén dữ liệu, giới thiệu khái niệm 'cửa sổ trượt' và cải thiện đáng kể tỷ lệ nén so với các thuật toán trước đó Thuật toán này sử dụng một từ điển động, bao gồm các bộ ba biểu diễn offset, độ dài chạy và ký tự lệch Offset là khoảng cách từ đầu tệp đến vị trí bắt đầu của cụm từ, trong khi độ dài chạy là số ký tự tiếp theo thuộc về cụm từ đó Ký tự lệch chỉ ra rằng một cụm từ mới đã được tìm thấy, kết hợp từ offset đến offset + length với ký tự lệch Cửa sổ trượt, có thể lên đến 64MB, cho phép từ điển chứa các mục cho dữ liệu đầu vào trước đó Ý tưởng chính của thuật toán là chia chuỗi nhập thành hai phần: bộ nhớ tạm tìm kiếm (search buffer) bên trái, chứa phần đã được mã hóa, và bộ nhớ nhìn về phía trước (look-ahead buffer) bên phải, chứa phần sẽ được mã hóa tiếp theo.
Với dữ liệu đầu vào là "abbadabba", kết quả đầu ra sẽ trông giống như
"abb(0,1,'d')(0,3,'a')" như trong ví dụ dưới đây:
Chức vụ Biểu tượng Đầu ra
Bảng 2:Ví dụ về thuật toán LZ77
Mặc dù sự thay thế này lớn hơn một chút so với dữ liệu đầu vào, nhưng nó đạt được kết quả nhỏ hơn đáng kể khi dữ liệu đầu vào dài hơn Để diễn đạt các bit lặp lại, LZ77 sử dụng một thẻ (token) bao gồm: số lượng ký tự trở về, độ dài ký tự trước, và kiểu ký tự tại vị trí i.
Giả sử ta có chuỗi ký tự sau: sir_sid sir_sid => (0, 0, “s”) s ir_sid =>(0, 0, “i”) si r_sid =>(0, 0,“r”) sir _sid =>(0, 0,“_”) sir_ sid =>(4, 2,“d”)
Bảng 3:Thuật toán LZ77 nén
Thuật toán LZ77 duyệt qua các phần tử chưa có trong từ điển Các ký tự trong chuỗi "sir" như "s", "i", "r", "_" đều chưa có, do đó sẽ được tạo mới Cả hai phần sẽ trở về ký tự trước, với độ dài ký tự trước có giá trị bằng.
0 Khi duyệt đến ký tự ‘s’ của chuỗi con thứ hai “sid”, ta nhận thấy chuỗi ký tự “si” đã xuất hiện trước đó.
Bảng 4:Index trong mảng ký tự
Từ giá trị “s” ở vị trí index thứ 4, ta đếm ngược bốn ký tự để đến vị trí index thứ 0 Tại vị trí này, ta lấy hai ký tự “s” và “i” Do đó, token sẽ là (4, 2, “d”), cho phép ta lấy chuỗi con ‘si’ và thêm ký tự “d” để tạo thành chuỗi con “sid”.
Để giải mã ngược, ta sử dụng các token để tái tạo lại chuỗi với định dạng (0, 0, “substring”) Tiếp theo, ta sẽ đếm ngược n ký tự từ vị trí i và lấy m ký tự giống như trước.
Bảng 5:Bảng giải mã ngược lại LZ77
2.3.1:Độ phức tạp của LZ77:
LZ77 sử dụng một cửa sổ trượt bao gồm hai phần: bộ nhớ tạm tìm kiếm và bộ nhớ nhìn trước, với kích thước cửa sổ được ký hiệu là W.
LZ77 tìm chuỗi dài nhất trong cửa sổ trượt cho mỗi vị trí trong dữ liệu đầu vào, khớp với dữ liệu phía trước Nếu không được tối ưu, quá trình tìm kiếm này có thể tốn O(W) cho mỗi vị trí.
Với n ký tự,tổng chi phí là O(nW).Nếu W lớn ,chi phí này rất cao.
Tuy nhiên, các triển khai thực tế thường dùng cấu trúc dữ liệu như trie hoặc hash table để tăng tốc tìm kiếm, giảm chi phí xuống O(n) trung bình.
Giải nén LZ77 là một quá trình đơn giản, chỉ cần đọc các token và sao chép chuỗi từ vị trí trước đó Chi phí cho quá trình này là O(n), trong đó n là độ dài của dữ liệu gốc.
Tổng quan: Độ phức tạp của LZ77 là O(nW) trong trường hợp xấu nhất,nhưng trong thực tế thường gần O(n) với tối ưu hóa.
Thuật toán lớp Entropy: Mã Huffman
Thuật toán Huffman, được phát triển bởi giáo sư David Huffman vào năm 1952 tại MIT, là một trong những thuật toán nén sớm nhất Mặc dù hiện nay không còn được ưa chuộng trong các chương trình nén file, thuật toán này vẫn đóng vai trò quan trọng, là nền tảng cho nhiều thuật toán nén hiện đại.
3.2 Ý tưởng thuật toán Ý tưởng thuật toán Huffman là xác định mã từ đầu ra dựa trên xác suất phân phối Như vậy thuật toán này sử dụng cơ chế mã Entropy – Prefix code Sự khác biệt của Huffman nằm ở chỗ, thuật toán này sẽ dựa vào thống kê ban đầu, tức là thay vì dùng mã ASCII tạo sẵn có 8 bit / ký tự thì bây giờ nếu ký tự đó xuất hiện thì mới ghi nhận lại Cũng như mã tiền tố làm giảm bớt số lượng ký tự để giảm số bit nhiều nhất có thể, thì mã Huffman sẽ làm giảm số bit các ký tự gặp nhiều nhất, tăng dần số bit cho các ký tự ít gặp hơn Độ phức tạp thuật toán này là O(nlogn) Ý tưởng chính hiện thực thuật toán này là tạo ra một cấu trúc cây, sau đó duyệt lần lượt các mã với các giá trị 0 nằm bên trái biểu thị tỉ lệ nhỏ hơn và 1 nằm bên phải biểu thị tỉ lệ lớn hơn
Giả sử ta có một chuỗi bất kỳ, và chuỗi đó đã được thống kê lại.
Ký tự Tỉ lệ(tần số xuất hiện)
Bảng 6:Bản thống kê tần số ban đầu
Sau đó ta tạo cặp các số có số lượng thống kê ít nhất thành tổng hai nút, nút nhỏ, nút lớn.
Ký tự Tỉ lệ(tần số xuất hiện)
Bảng 7:Tạo cặp và tính tổng
Ký tự Tỉ lệ(tần số xuất hiện)
Bảng 8:Tạo cặp và tính tổng
Ký tự Tỉ lệ(tần số xuất hiện)
Bảng 9:Tạo cặp và tính tổng
Ký tự Tỉ lệ(tần số xuất hiện)
Bảng 10:Tạo cặp và tính tổng
Ký tự Tỉ lệ(tần số xuất hiện)
Bảng 11:Tạo cặp và tính tổng
Tạo cây từ các bảng trên:
Để tạo ra chữ “A”, cần duyệt các tiền tố bit của các nút khác trước khi thêm bit của “A” Trong phương pháp Huffman, sau khi hoàn thành việc tạo cây, cây này sẽ được lưu trữ để sử dụng cho quá trình giải nén sau này Việc giải nén bằng Huffman chỉ cần cây đã được lưu, sau đó duyệt lại các phần tử trên cây để thực hiện giải mã.
Hình 2:Lưu đồ thuật toán nén Huffman
Hình 3:Lưu đồ thuật toán giải mã Huffman
3.3: Độ phức tạp của Huffman Coding
Xây dựng cây Huffman: Đầu tiên,thống kê tần suất ký tự:O(n) ,với n là độ dài dữ liệu đầu vào.
Sử dụng hàng đợi ưu tiên để xây dựng cây Huffman với k ký tự khác nhau (thường k ≤ 256 với bảng mã ASCII) là một phương pháp hiệu quả Mỗi thao tác thêm hoặc lấy phần tử nhỏ nhất từ hàng đợi ưu tiên tốn O(log k), và thực hiện k – 1 thao tác gộp nút dẫn đến tổng chi phí là O(k log k) Khi k nhỏ hơn nhiều so với n, chi phí này thường không đáng kể so với O(n).
Tổng độ phức tạp xây dựng cây: O(n + k log k),thường xấp xỉ O(n) khi k nhỏ.
Sau khi xây dựng cây, mỗi ký tự được mã hóa bằng cách duyệt từ gốc đến lá, với chi phí trung bình là O(log k) cho mỗi ký tự Đối với n ký tự, tổng chi phí sẽ là O(n log k), xấp xỉ O(n log n) trong trường hợp xấu nhất.
Giải nén dữ liệu nén bằng cách duyệt từng bit và sử dụng cây Huffman để tái tạo ký tự Với m bit trong dữ liệu nén (thường m ≤ n log k), chi phí thực hiện là O(m), xấp xỉ O(n log k).
Tổng quan:Độ phức tạp tổng thể của Huffman là O(n log n),chủ yếu đến từ giai đoạn mã hóa.
Thư viện zlib là một thư viện có sẵn trong Python, cho phép nén và giải nén dữ liệu bằng thuật toán DEFLATE, kết hợp giữa LZ77 và Huffman.
Thư viện zlib sử dụng thuật toán DEFLATE (kết hợp LZ77 và
Mặc dù Huffman có hiệu quả trong việc nén dữ liệu, nhưng khi so sánh với LZ77 thuần, hiệu quả nén trên văn bản dài không đạt mức cao Nguyên nhân có thể là do zlib được tối ưu hóa cho dữ liệu tổng quát hơn là cho văn bản có tính lặp lại cao.
Tuy nhiên, zlib có ưu điểm là dễ sử dụng, tích hợp sẵn trong Python và phù hợp cho nhiều loại dữ liệu khác nhau (văn bản, ảnh, v.v.).
4.1 Độ phức tạp của zlib(DEFLATE):
Zlib kết hợp cả 2 LZ77 và Huffman,nên độ phức tạp là tổ hợp của cả hai: +,Giai đoạn LZ77:O(n) với triển khai tối ưu.
+,Giai đoạn Huffman:O(n log k),với k là số khối dữ liệu sau LZ77.
Tổng chi phí:Gần O(n),nhờ tối ưu hóa trong triển khai thực tế của zlib. 4.2 So sánh độ phực tạp:
Thuật toán Nén(trường hợp xấu nhất)
Nén (thực tế) Giải nén
Bảng 12:So sánh độ phức tạp của 3 thuật toán(Huffman,LZ77,zlib)
Huffman có độ phức tạp phụ thuộc vào số lượng ký tự khác nhau(k),thường hiệu quả về mặt lý thuyết nhưng chậm hơn trên dữ liệu lớn.
LZ77 phụ thuộc vào kích thước cửa sổ (W),có thể chậm nếu không tối ưu,nhưng triển khai thực tế(như trong zlib) rất nhanh.
Zlib tận dụng cả hai thuật toán và được tối ưu hóa,nên có hiệu suất thực tế tốt nhất về mặt thời gian.
SO SÁNH HIỆU SUẤT GIỮA CÁC PHƯƠNG PHÁP NÉN(HUFFMAN VỚI LZ77)
Các bước thử nghiệm thuật toán Huffman trên văn bản tiếng Việt
Bước 1: Chuẩn bị văn bản tiếng Việt
Chọn một đoạn văn bản tiếng Việt có dấu từ bài báo, sách hoặc truyện ngắn, đảm bảo đoạn văn có độ dài phù hợp để sử dụng.
(khoảng 1000 ký tự trở lên) để thấy rõ hiệu quả nén.
Lưu văn bản: Ghi đoạn văn bản này vào một file, ví dụ input.txt Hãy đảm bảo file được mã hóa UTF-8 để hỗ trợ tiếng Việt.
Ví dụ văn bản :đưa văn bản vào file input.txt tự tạo
Nén dữ liệu là khái niệm quan trọng trong thời đại công nghệ thông tin, giúp lưu trữ và truyền tải dữ liệu hiệu quả, tiết kiệm tài nguyên và tăng tốc độ xử lý Thuật toán nén như Huffman coding giảm dung lượng dữ liệu mà không mất thông tin, tối ưu hóa quá trình lưu trữ và truyền tải.
Bước 2: Triển khai thuật toán Huffman:
Các bước thử nghiệm thuật toán LZ77trên văn bản tiếng Việt
Tổng kết: So sánh LZ77 vs Huffman:
Thuật toán Gốc (bytes) Nén(bytes) Tỷ lệ nén Hiệu quả nén
Bảng 13:So sánh giữa 2 thuật toán LZ77 VÀ Huffman
-LZ77 nén tốt hơn Huffman rất nhiều trong trường hợp văn bản lớn có tính lặp lại cao (như đoạn bạn tạo bằng lặp).
-Huffman nén kém hơn đáng kể trong tình huống này, vì nó không tận dụng sự lặp lại theo chuỗi, mà chỉ dựa vào tần suất byte.
Với văn bản dài và lặp dùng LZ77 là lựa chọn tối ưu hơn Huffman. 2.Đoạn text ngắn:
Ví dụ: text: “1 dữ liệu bí mật mà tôi đang sở hữu”
Kết quả cuối cùng sau khi chạy:
Thuật toán Tỷ lệ nén Dung lượng sau nén
Thời gian nén Giải nén đúng
Bảng 14:So sánh kết quả của 2 thuật toán với đoạn text ngắn
-Huffman hiệu quả hơn LZ77 về tỷ lệ nén và cả tốc độ.
-LZ77 chỉ hơn trong các trường hợp:
+,Dữ liệu lặp lại theo pattern dài, ví dụ "abcabcabcabc ".
+,Hoặc cần thuật toán đơn giản, dễ cài đặt thủ công (không dùng bit-level mã hóa như Huffman).
3,Ứng dụng nén ảnh JPEG
Bước 1:Cài đặt thư viện “Pillow”, “numpy”.Chuẩn bị sẵn 1 ảnh để test.
Bước 3:Nghiệm thu kết quả:
Có thể thấy ảnh từ 153KB đã thành 55KB là đã nén thành công.
KẾT
Báo cáo nghiên cứu chuyên sâu về hai thuật toán nén dữ liệu không mất mát, bao gồm Huffman Coding và LZ77, với cơ sở lý thuyết, triển khai và ứng dụng thực tiễn Huffman Coding sử dụng mã hóa entropy dựa trên tần suất ký tự để giảm số bit, trong khi LZ77 áp dụng cơ chế cửa sổ trượt để nén dữ liệu dựa trên các mẫu lặp lại Thư viện zlib (DEFLATE) được tích hợp để thử nghiệm, kết hợp cả hai thuật toán, mang lại hiệu quả nén ổn định nhưng không vượt trội so với LZ77 trong trường hợp văn bản lặp Các thử nghiệm trên văn bản tiếng Việt, đoạn văn ngắn và ảnh JPEG đã được thực hiện.
LZ77 nổi bật hơn so với Huffman và zlib với tỷ lệ nén 89.84%, vượt xa 55.74% của Huffman và 40.4% của zlib Điều này đặc biệt hiệu quả trong các trường hợp dữ liệu có tính lặp lại cao, nhờ vào khả năng nhận diện và mã hóa chuỗi lặp.
Huffman nén dữ liệu hiệu quả hơn LZ77 với tỷ lệ nén đạt 0.500 so với 0.676, đồng thời có tốc độ xử lý nhanh hơn, đặc biệt phù hợp cho dữ liệu không có mẫu lặp rõ ràng.
Nén ảnh JPEG sử dụng các kỹ thuật nén, bao gồm cả Huffman, đã giúp giảm dung lượng ảnh từ 153KB xuống chỉ còn 55KB, cho thấy hiệu quả rõ rệt trong ứng dụng thực tế.
Huffman có độ phức tạp O(n log n), trong khi LZ77 có độ phức tạp O(nW), gần thực tế là O(n) Zlib đạt hiệu suất thực tế O(n), phù hợp cho dữ liệu tổng quát.
Huffman là phương pháp nén hiệu quả cho dữ liệu có phân bố tần suất ký tự không đồng đều và yêu cầu tốc độ xử lý nhanh Ngược lại, LZ77 và zlib được tối ưu hóa cho dữ liệu có tính lặp lại cao và khối lượng lớn.
Cả ba phương pháp đều đóng vai trò quan trọng trong lý thuyết thông tin và ứng dụng thực tiễn như lưu trữ, truyền tải dữ liệu, và quản lý Big Data Nghiên cứu này làm rõ nguyên lý hoạt động và cung cấp cơ sở để lựa chọn thuật toán nén dữ liệu phù hợp, đồng thời mở ra hướng phát triển cho các nghiên cứu tiếp theo về tối ưu hóa hiệu suất nén.