Huffman coding là gì? Thuật toán nén dữ liệu theo tần suất ký tự

Huffman coding là gì? Thuật toán nén dữ liệu theo tần suất ký tự

Sơ đồ cây Huffman dựng từ tần suất bốn ký tự, mỗi lá là một ký tự kèm xác suất và nhánh nhị phân tương ứng
Sơ đồ cây Huffman dựng từ tần suất bốn ký tự, mỗi lá là một ký tự kèm xác suất và nhánh nhị phân tương ứng

Nguyên lý cốt lõi

Trước Huffman coding, cách mã hoá cố định được dùng: mỗi ký tự luôn chiếm cùng số bit, ví dụ 8 bit cho ASCII. Cách này lãng phí rõ ràng khi văn bản tiếng Việt hay văn bản HTML chứa một ký tự lặp đi lặp lại hàng nghìn lần cùng với những ký tự chỉ xuất hiện đúng một lần.

Cây Huffman hoàn chỉnh với các nút trung gian và độ dài mã bit của từng ký tự ở hai lá
Cây Huffman hoàn chỉnh với các nút trung gian và độ dài mã bit của từng ký tự ở hai lá

Các bước dựng cây Huffman

Cây Huffman là một cây nhị phân đầy đủ, trong đó mỗi lá là một ký tự và trọng số của lá bằng tần suất xuất hiện. Thuật toán dựng cây theo thuật toán tham lam (greedy), chỉ bốn bước lặp:

  1. Khởi tạo: tạo một nút lá cho mỗi ký tự, gán trọng số bằng tần suất xuất hiện của ký tự đó trong dữ liệu.
  2. Chọn hai nút nhẹ nhất: lấy hai nút có trọng số nhỏ nhất đang nằm trong hàng đợi ưu tiên (min-heap).
  3. Gộp: tạo một nút cha mới, đặt hai nút vừa chọn làm hai lá của nó, trọng số của cha bằng tổng hai trọng số con. Đưa nút cha trở lại hàng đợi.
  4. Lặp lại cho tới khi hàng đợi chỉ còn đúng một nút. Nút đó là gốc cây, và nó chính là nút gốc.
Minh hoạ từng bước gộp hai ký tự tần suất thấp nhất khi mã hoá chuỗi văn bản bằng Huffman
Minh hoạ từng bước gộp hai ký tự tần suất thấp nhất khi mã hoá chuỗi văn bản bằng Huffman

Xét chuỗi mẫu kinh điển beep boop beer! gồm 16 ký tự. Đếm tần suất ta có: e xuất hiện 6 lần, b 4 lần, khoảng trắng 3 lần, o 2 lần, p 2 lần, và các ký tự r, ! mỗi ký tự 1 lần. Tổng cộng lại đúng 16 lá.

Dựng cây theo quy trình trên, ký tự e đứng sâu nhất nên mã của nó chỉ dài 1 bit (0), còn các ký tự hiếm như ! nằm gần gốc nên mã dài 6 bit. Khi nối lại toàn bộ, chuỗi gốc 16 ký tự với 16 byte dữ liệu được rút xuống còn khoảng 40 bit, tức giảm hơn một nửa.

Huffman coding kết hợp với mã hoá tiền tố

Điểm yếu cố hữu của Huffman coding là nó tạo ra một bảng mã riêng cho từng tập dữ liệu. Nếu tần suất phân bố kém, hiệu quả nén suy giảm rõ rệt; với dữ liệu đã nén sẵn, thuật toán thậm chí còn làm file lớn thêm. Vì vậy, các định dạng nén hiện đại không dùng Huffman độc lập mà ghép nó với mã hoá chuỗi (run-length) và mã hoá theo thứ tự xuất hiện.

Định dạng Cách dùng Huffman Phạm vi áp dụng
ZIP / DEFLATE Huffman cho ký tự chữ và ký tự khác nhau, cùng bảng chuỗi khoảng trắng Tệp nén tổng quát
GZIP DEFLATE kết hợp LZ77 Tệp nén Unix
PNG Huffman cho dữ liệu pixel đã lọc Ảnh PNG
JPEG Huffman cho hệ số lượng tử hoá từng khối 8×8 Ảnh JPEG
MP3 / AAC Huffman cho dữ liệu âm thanh đã nén theo băng tần số Âm thanh số

Ưu điểm và hạn chế

Ưu điểm:

  • Đơn giản, dễ cài đặt, thời gian nén và giải nén đều là O(n log n).
  • Không cần bảng tra cứu phức tạp: cây nhị phân biểu diễn gọn gàng bảng mã.
  • Phù hợp với dữ liệu văn bản, mã nguồn, log, dữ liệu phân phối không đều.

Hạn chế:

  • Không nén được dữ liệu phân phối đều, tỷ lệ nén gần bằng không.
  • Là thuật toán một lớp, mỗi tập dữ liệu cần một cây riêng, bảng mã phải lưu kèm tệp nén.
  • Không phù hợp với dữ liệu đã chuẩn hoá, ví dụ video hoặc ảnh JPEG đã nén.

Khi nào nên dùng Huffman coding

Nếu bạn đang xây dựng một hệ thống lưu trữ hoặc truyền dữ liệu văn bản, hãy dùng sẵn zlib hay gzip thay vì tự hiện thực. Ngược lại, khi cần thuật toán tối ưu cho một bộ ký tự cố định, ví dụ nén ký tự trong file log, hay khi thiết kế giao thức truyền dữ liệu trên mạng với băng thông hạn chế, Huffman coding là lựa chọn chuẩn và rẻ tiền nhất.

Tham khảo thêm: Huffman coding trên Wikipedia, và bảng đặc tả gói tin trong đặc tả DEFLATE (RFC 1951) giải thích cách Huffman được dùng trong ZIP.

Tôi là một lập trình viên IOS. Code chính là IOS nhưng thỉnnh thoảng vẫn đá sang Android hoặc web. Mặc dù không quá thông thạo nhưng tôi sẽ chia sẻ những kiến thức mà mình đã tìm hiểu, áp dụng qua.

Bài viết liên quan

Định lý CAP là gì? Tính nhất quán, sẵn sàng và phân vùng mạng

Định lý CAP là nguyên lý nền tảng trong thiết kế hệ thống phân tán, phát biểu rằng một cơ sở dữ liệu phân tán không thể đồng thời bảo…

Xem thêm

HyperLogLog là gì? Cấu trúc dữ liệu xác suất đếm phần tử duy nhất

HyperLogLog là gì? Cấu trúc dữ liệu xác suất để ước lượng unique HyperLogLog (HLL) là một cấu trúc dữ liệu xác suất được thiết kế để ước lượng số…

Xem thêm

Dependency Injection là gì? Nguyên tắc tiêm phụ thuộc trong lập trình

Dependency Injection (DI) là một nguyên tắc thiết kế phần mềm trong đó các đối tượng không tự tạo ra phụ thuộc của chúng, mà nhận phụ thuộc từ bên…

Xem thêm
0 0 đánh giá
Article Rating
Theo dõi
Thông báo của
guest
0 Comments
Cũ nhất
Mới nhất Được bỏ phiếu nhiều nhất