--- ## 一、Huffman 編碼 **Huffman 編碼**是一種無損數據壓縮技術,通過分配變長的前綴碼來表示不同的符號,從而根據符號的出現頻率實現壓縮。頻率較高的符號分配較短的碼,頻率較低的符號分配較長的碼。 ### 流程步驟: 1. **計算符號頻率**: - 分析待編碼數據,統計每個符號出現的次數或概率。 - 例如,對字符串 `"ABBCCC"` 進行頻率統計: - A: 1 次 - B: 2 次 - C: 3 次 2. **建立優先隊列(最小堆)**: - 將所有符號作為節點,根據頻率從小到大排列,形成優先隊列。 - 使用最小堆結構以便於每次取出頻率最低的兩個節點。 3. **構建 Huffman 樹**: - 當隊列中節點數量大於1時,重複以下步驟: - 從隊列中取出兩個頻率最低的節點。 - 創建一個新的內部節點,其頻率為這兩個節點頻率之和,並將這兩個節點作為新節點的左右子節點。 - 將新節點插回隊列中。 - 重複此過程,直到只剩下一個節點,該節點即為 Huffman 樹的根節點。 4. **生成編碼**: - 從根節點開始,左分支標記為 '0',右分支標記為 '1',遍歷整棵樹,為每個葉子節點(即原始符號)分配一個唯一的二進制碼。 ### 詳細範例: 假設有五個符號及其頻率: | 符號 | 頻率 | |------|------| | A | 5 | | B | 9 | | C | 12 | | D | 13 | | E | 16 | | F | 45 | #### 步驟 1:初始化優先隊列 ``` A(5), B(9), C(12), D(13), E(16), F(45) ``` #### 步驟 2:構建 Huffman 樹 1. 合併 A(5) 和 B(9) → AB(14) 2. 合併 AB(14) 和 C(12) → ABC(26) 3. 合併 D(13) 和 E(16) → DE(29) 4. 合併 ABC(26) 和 DE(29) → ABCDE(55) 5. 合併 ABCDE(55) 和 F(45) → ABCDEF(100) #### 步驟 3:生成編碼 ```mermaid graph TD ABCDEF["ABCDE (100)"] ABCDE["ABCDE (55)"] F["F (45)"] ABC["ABC (26)"] DE["DE (29)"] AB["AB (14)"] C["C (12)"] D["D (13)"] E["E (16)"] A["A (5)"] B["B (9)"] ABCDEF --> ABCDE ABCDEF --> F ABCDE --> ABC ABCDE --> DE ABC --> AB ABC --> C DE --> D DE --> E AB --> A AB --> B ``` #### 步驟 4:分配編碼 - 從根節點開始: - 左分支標記為 '0',右分支標記為 '1' | 符號 | 編碼 | |------|-------| | F | 1 | | A | 000 | | B | 001 | | C | 01 | | D | 100 | | E | 101 | ### 解碼過程: 給定編碼 `00000101100101`: 1. **000** → A 2. **001** → B 3. **01** → C 4. **100** → D 5. **101** → E 解碼結果:`ABCDE` ### Huffman 編碼的優點與缺點: **優點**: - **最優性**:在符號獨立且符號頻率已知的情況下,Huffman 編碼是最優的前綴碼編碼。 - **效率高**:能有效減少平均編碼長度,特別是當符號頻率差異較大時。 **缺點**: - **靜態性**:需要在編碼前預先知道符號的頻率,對於動態或未知頻率的數據不適用。 - **不適合連續符號依賴**:不考慮符號之間的依賴關係。 ### Huffman 編碼的應用場景: - **壓縮文件格式**:如 ZIP、JPEG 等。 - **通信協議**:在帶寬受限的環境中減少數據傳輸量。 --- ## 二、自適應 Huffman 編碼 **自適應 Huffman 編碼**(Adaptive Huffman Coding)是一種動態調整 Huffman 樹的編碼方法,適用於流式數據或無法預先獲取符號頻率的情況。它在編碼過程中即時更新樹結構,以適應符號頻率的變化。 ### 流程步驟: 1. **初始化**: - 建立一個特殊的「尚未傳輸」節點(NYT,Not Yet Transmitted),代表尚未出現的符號。 - 初始時,樹只有一個 NYT 節點。 2. **逐符號處理**: - 對於每個輸入符號,進行以下處理: - **新符號**: - 發送 NYT 節點的當前編碼,然後發送符號的固定長度二進制表示(例如,8 位)。 - 將新符號加入 Huffman 樹中,並更新樹結構。 - **已出現符號**: - 發送該符號當前在 Huffman 樹中的編碼。 - 增加該符號的頻率,並根據需要調整樹結構以保持 Huffman 樹的屬性。 3. **更新樹結構**: - 使用特定的算法(如 FGK 或 Vitter 算法)來更新樹,使得頻率增加的符號移動到更高的節點,維持樹的最優性。 ### FGK 算法示意: **FGK(Faller-Gallager-Knuth)算法**是自適應 Huffman 編碼中常用的更新算法。其主要步驟包括: 1. **尋找節點**: - 對於當前符號,找到其在樹中的節點。 - 如果是新符號,則創建新的節點並插入。 2. **交換節點**: - 為了維持樹的順序性,可能需要交換節點的位置。 - 確保每個節點的權重(頻率)滿足從上到下、從左到右的順序。 3. **增量更新**: - 增加該符號的頻率。 - 遍歷從葉節點到根節點的路徑,更新每個節點的頻率並調整樹結構。 ### 詳細範例: 考慮輸入序列 `"ABACA"`,逐步構建自適應 Huffman 樹。 #### 步驟 1:初始化 - 樹僅有 NYT 節點。 #### 步驟 2:處理第一個符號 'A' - **新符號**: - 發送 NYT 的編碼(空碼) + 'A' 的固定編碼(假設 8 位)`01000001`。 - 更新樹:NYT → A 和新的 NYT。 ```mermaid graph TD Root["Root"] NYT1["NYT"] A["A (1)"] Root --> A Root --> NYT1 ``` #### 處理第二個符號 'B' - **新符號**: - 發送 NYT1 的編碼 `1` + 'B' 的固定編碼 `01000010`。 - 更新樹:Root → A 和 B,然後再添加 NYT。 ```mermaid graph TD Root["Root"] A["A (1)"] B["B (1)"] NYT2["NYT"] Root --> A Root --> B B --> NYT2 ``` #### 處理第三個符號 'A' - **已出現符號**: - 發送 'A' 的編碼 `0`。 - 增加 'A' 的頻率至 2。 - 更新樹結構,使 'A' 位於更高的位置。 ```mermaid graph TD Root["Root"] A["A (2)"] B["B (1)"] NYT2["NYT"] Root --> A Root --> B B --> NYT2 ``` #### 處理第四個符號 'C' - **新符號**: - 發送 NYT2 的編碼 `11` + 'C' 的固定編碼 `01000011`。 - 更新樹:插入 'C' 並調整頻率。 ```mermaid graph TD Root["Root"] A["A (2)"] B["B (1)"] C["C (1)"] NYT3["NYT"] Root --> A Root --> B B --> C B --> NYT3 ``` #### 處理第五個符號 'A' - **已出現符號**: - 發送 'A' 的編碼 `0`。 - 增加 'A' 的頻率至 3。 - 更新樹結構。 ```mermaid graph TD Root["Root"] A["A (3)"] B["B (1)"] C["C (1)"] NYT3["NYT"] Root --> A Root --> B B --> C B --> NYT3 ``` ### 自適應 Huffman 編碼的優點與缺點: **優點**: - **適應性強**:不需要事先知道符號頻率,適用於動態數據。 - **即時壓縮**:適合流式數據壓縮,如實時傳輸。 **缺點**: - **計算複雜度高**:每處理一個符號都需要更新樹結構,增加了計算負擔。 - **初始階段壓縮效率低**:由於頻率信息不足,初始壓縮效率可能較低。 ### 自適應 Huffman 編碼的應用場景: - **實時數據壓縮**:如視頻流、音頻流。 - **無法預測數據頻率的情況**:如加密通信。 ### 進階圖示:自適應 Huffman 編碼的動態更新 ```mermaid graph TD Root1["Root"] NYT1["NYT"] Root1 --> NYT1 %% 插入A Root2["Root"] A1["A (1)"] NYT2["NYT"] Root2 --> A1 Root2 --> NYT2 %% 插入B Root3["Root"] A1["A (1)"] B1["B (1)"] NYT3["NYT"] Root3 --> A1 Root3 --> B1 B1 --> NYT3 %% 更新A frequency Root4["Root"] A2["A (2)"] B1["B (1)"] NYT3["NYT"] Root4 --> A2 Root4 --> B1 B1 --> NYT3 %% 插入C Root5["Root"] A2["A (2)"] B1["B (1)"] C1["C (1)"] NYT4["NYT"] Root5 --> A2 Root5 --> B1 B1 --> C1 B1 --> NYT4 %% 更新A frequency Root6["Root"] A3["A (3)"] B1["B (1)"] C1["C (1)"] NYT4["NYT"] Root6 --> A3 Root6 --> B1 B1 --> C1 B1 --> NYT4 ``` --- ## 三、Tunstall 編碼 **Tunstall 編碼**是一種將變長符號序列(短語)映射到固定長度二進制碼的編碼方法。與 Huffman 編碼不同,Tunstall 編碼將變長的短語編碼為固定長度的碼,這在某些應用場景中具有優勢,如需要固定碼長的硬件實現。 ### 流程步驟: 1. **初始化**: - 建立一個單一的根節點,代表空字符串。 2. **生成短語樹**: - 持續擴展樹,將最不可能的葉節點擴展為其所有可能的子節點,根據符號的概率進行分支。 - 這一過程持續進行,直到達到所需的固定碼長,即生成的短語數量等於 \(2^n\),其中 \(n\) 是固定碼長。 3. **分配固定長碼**: - 為每個生成的短語分配一個唯一的固定長度二進制碼。 4. **編碼與解碼**: - **編碼**:將輸入數據分割為預先定義的短語,然後用對應的固定長碼替換。 - **解碼**:將固定長碼轉換回相應的短語,並重組為原始數據。 ### 詳細範例: 假設符號集為 {A, B, C},並假設每個符號的概率為: | 符號 | 機率 | |------|-------| | A | 0.5 | | B | 0.3 | | C | 0.2 | 選擇固定碼長 \(n = 2\),即需要 \(2^2 = 4\) 個短語。 #### 步驟 1:初始化 - 根節點為空字符串 `""`。 #### 步驟 2:生成短語樹 1. 將根節點展開,生成所有可能的第一級短語: - A - B - C 2. 根據機率選擇最不可能的短語進行擴展,直到生成 4 個短語: - 展開 C(機率最低 0.2),生成 CA 和 CB - 現在短語集為:A, B, CA, CB #### 步驟 3:分配固定長碼 | 短語 | 固定長碼 | |------|----------| | A | 00 | | B | 01 | | CA | 10 | | CB | 11 | #### 步驟 4:編碼與解碼 **編碼示例**: 輸入序列 `"ACAABCB"` 分割為短語: - "A", "C", "AA", "B", "CB" 由於 "AA" 不在短語集中,需進一步調整或重新定義短語集。為了保持固定碼長,通常需要更大的短語集或更高的固定碼長。 **調整後的短語集**(假設 \(n = 3\),需 \(2^3 = 8\) 個短語): 1. A, B, C 2. 展開最不可能的 C → CA, CB 3. 展開 CA → CAA, CAB 4. 展開 CB → CBA, CBB 最終短語集:A, B, CAA, CAB, CBA, CBB, CA, CB **分配固定長碼**: | 短語 | 固定長碼 | |------|----------| | A | 000 | | B | 001 | | CA | 010 | | CB | 011 | | CAA | 100 | | CAB | 101 | | CBA | 110 | | CBB | 111 | **編碼示例**: 輸入 `"ACAABCB"` 分割為: - "A", "C", "A", "A", "B", "C", "B" 根據短語集,可分割為: - "A", "CA", "A", "B", "CB" 對應編碼為: - "000", "010", "000", "001", "011" 最終編碼:`000 010 000 001 011` ### Tunstall 編碼的優點與缺點: **優點**: - **固定碼長**:所有短語的編碼長度相同,便於硬件實現和並行處理。 - **高壓縮效率**:通過使用變長短語,可以有效地壓縮具有高頻率短語的數據。 **缺點**: - **靜態性**:需要在編碼前預先分析符號的頻率,並生成短語樹。 - **短語生成複雜度高**:生成短語樹可能較為複雜,特別是符號集較大時。 ### Tunstall 編碼的應用場景: - **硬件實現**:需要固定碼長的場景,如某些通信協議中的硬件編碼器。 - **並行處理**:固定碼長便於並行處理,提高編碼和解碼速度。 ### 進階圖示:Tunstall 編碼的短語樹 ```mermaid graph TD Root[""] A["A (0.5)"] B["B (0.3)"] C["C (0.2)"] Root --> A Root --> B Root --> C C --> CA["CA (0.2)"] C --> CB["CB (0.2)"] CA --> CAA["CAA (0.2)"] CA --> CAB["CAB (0.2)"] CB --> CBA["CBA (0.2)"] CB --> CBB["CBB (0.2)"] ``` --- ## 四、Golomb 編碼 **Golomb 編碼**是一種無損編碼方法,特別適用於服從幾何分布或類似稀疏分布的整數。它通過將整數分為商和餘數兩部分來實現高效編碼,特別適合於編碼運行長度或具有重複模式的數據。 ### 流程步驟: 1. **選擇參數 \(m\)**: - 根據數據的分布特性選擇適當的參數 \(m\)。 - \(m\) 通常是正整數,決定了編碼的分割方式。 - 對於幾何分布,最佳 \(m\) 能夠最小化平均編碼長度。 2. **編碼每個整數 \(k\)**: - 對於每個待編碼的正整數 \(k\): - 計算商 \(q = \lfloor k / m \rfloor\)。 - 計算餘數 \(r = k \mod m\)。 - 編碼方式: - **商 \(q\)**:使用 \(q\) 個 '1',後接一個 '0' 作為分隔符。 - **餘數 \(r\)**: - 如果 \(m\) 是 \(2^n\),則使用固定長度的 \(n\) 位二進制表示。 - 如果 \(m\) 不是 \(2^n\),則使用更複雜的餘數編碼方法,如使用不等長度的碼或擴展碼來保持唯一性。 3. **組合編碼**: - 將商的編碼與餘數的編碼連接起來,形成最終的 Golomb 編碼。 ### 詳細範例: 假設選擇 \(m = 3\),編碼數字 \(k = 7\): 1. **計算商和餘數**: - \(q = \lfloor 7 / 3 \rfloor = 2\) - \(r = 7 \mod 3 = 1\) 2. **編碼商 \(q = 2\)**: - 使用兩個 '1',後接一個 '0' → `'110'` 3. **編碼餘數 \(r = 1\)**: - 由於 \(m = 3\) 不是 \(2^n\),需要使用更複雜的餘數編碼。 - Golomb 編碼通常採用對 \(r\) 進行分段編碼,確保唯一性。 - 例如,餘數 \(r\) 的編碼可以使用如下方式: - 當 \(m\) 不是 \(2^n\) 時,可以使用“截斷法”: - 計算 \(k = \lceil \log_2 m \rceil = 2\) 位。 - 如果 \(r < m - 2^k\), 使用 \(k\) 位表示。 - 否則,重新編碼餘數。 - 具體實現視需求而定。 4. **最終編碼**: - 商的編碼 `'110'` + 餘數的編碼 `'01'`(假設使用 2 位表示) → `'11001'` ### Golomb 編碼的優點與缺點: **優點**: - **高效性**:對於幾何分布或類似稀疏分布的數據,Golomb 編碼具有較高的壓縮效率。 - **參數靈活**:通過調整參數 \(m\),可適應不同的數據分布。 **缺點**: - **靜態性**:需要事先了解數據的分布特性以選擇合適的 \(m\)。 - **實現複雜度**:當 \(m\) 不是 \(2^n\) 時,餘數編碼變得複雜。 ### Golomb 編碼的應用場景: - **運行長度編碼**:如圖像壓縮中的連續相同像素值編碼。 - **無線通信**:特別是在數據稀疏的情況下,如稀疏信號壓縮。 ### 進階範例:不同 \(m\) 值的 Golomb 編碼 #### 範例 1:\(m = 2\)(Rice 編碼) 當 \(m = 2\) 時,Golomb 編碼稱為 **Rice 編碼**,其餘數編碼簡單為固定長度。 **編碼數字 \(k = 5\)**: 1. **計算商和餘數**: - \(q = \lfloor 5 / 2 \rfloor = 2\) - \(r = 5 \mod 2 = 1\) 2. **編碼商 \(q = 2\)**: - `'11'`(兩個 '1') + `'0'` → `'110'` 3. **編碼餘數 \(r = 1\)**: - 1 位二進制表示 → `'1'` 4. **最終編碼**: - `'110' + '1'` → `'1101'` #### 範例 2:\(m = 4\) **編碼數字 \(k = 6\)**: 1. **計算商和餘數**: - \(q = \lfloor 6 / 4 \rfloor = 1\) - \(r = 6 \mod 4 = 2\) 2. **編碼商 \(q = 1\)**: - `'1'`(一個 '1') + `'0'` → `'10'` 3. **編碼餘數 \(r = 2\)**: - \(m = 4\) 是 \(2^2\),使用 2 位二進制表示 → `'10'` 4. **最終編碼**: - `'10' + '10'` → `'1010'` ### Golomb 編碼的實現考量: - **選擇最佳 \(m\)**: - \(m\) 的選擇對壓縮效率至關重要。通常,根據數據的平均值和分布選擇 \(m\)。 - 常見方法包括使用最大似然估計來選擇最佳 \(m\)。 - **餘數編碼策略**: - 當 \(m\) 不是 \(2^n\) 時,需要使用更複雜的餘數編碼方法,如截斷法或擴展碼法,以確保餘數的唯一性和最小化編碼長度。 ### Golomb 編碼的應用實例: 考慮一個無線通信系統,其中大多數數據包的長度較短(稀疏分布)。使用 Golomb 編碼可以高效地壓縮這些數據包長度,減少傳輸所需的位數。 --- ## 五、差異化總結 以下表格總結了 **Huffman 編碼**、**自適應 Huffman 編碼**、**Tunstall 編碼** 及 **Golomb 編碼** 的主要特性與差異: | **特性** | **Huffman 編碼** | **自適應 Huffman 編碼** | **Tunstall 編碼** | **Golomb 編碼** | |------------------------|--------------------------------|-----------------------------------|----------------------------------|----------------------------------| | **編碼類型** | 變長前綴碼 | 變長前綴碼 | 變長短語到固定長碼 | 整數的無損編碼 | | **靜態 vs 動態** | 靜態(需預先統計頻率) | 動態(即時更新樹結構) | 靜態(根據頻率生成短語) | 靜態(根據分布選參數 \(m\)) | | **適用數據類型** | 一般符號集 | 流式數據或頻率未知的數據 | 需要固定碼長的應用場景 | 幾何分布或稀疏分布的整數 | | **壓縮效率** | 高,基於最佳前綴碼理論 | 隨數據量增大而提升 | 在固定碼長需求下效率高 | 對特定分布數據效率高 | | **實現複雜度** | 相對簡單 | 較高,需頻繁更新樹結構 | 中等,需生成短語樹 | 較低,主要依賴參數選擇 | | **延遲性** | 無 | 可能有初始壓縮效率低的延遲 | 無,編碼固定碼長 | 無 | | **靈活性** | 需要預先統計 | 高,適應不同數據流 | 受限於固定碼長 | 高,通過參數 \(m\) 調整 | | **最佳應用場景** | 文件壓縮、通信協議 | 實時數據壓縮、動態頻率數據 | 硬件實現、並行處理 | 運行長度編碼、無線通信 | ### 主要差異: 1. **編碼方式**: - **Huffman** 和 **自適應 Huffman** 都是基於頻率的變長前綴碼,但後者能動態調整。 - **Tunstall** 則是將變長的短語映射到固定長的碼,適用於需要固定碼長的應用場景。 - **Golomb** 主要針對特定分布的整數進行高效編碼,特別適合稀疏數據或運行長度編碼。 2. **靜態 vs 動態**: - **Huffman** 和 **Tunstall** 是靜態編碼,需要預先分析數據。 - **自適應 Huffman** 和 **Golomb** 可以根據數據實時調整或選擇參數(Golomb 的 \(m\) 雖然是靜態的,但能根據數據分布靈活選擇)。 3. **應用場景**: - **Huffman** 適用於大多數一般數據壓縮,如文件壓縮和通信協議。 - **自適應 Huffman** 適用於流式或未知頻率的數據,如實時傳輸和加密通信。 - **Tunstall** 適用於需要固定碼長的應用,如某些硬件實現和並行處理。 - **Golomb** 適用於具有幾何分布或類似稀疏分布的數據,如運行長度編碼和無線通信。 4. **壓縮效率與實現複雜度**: - **Huffman** 通常具有較高的壓縮效率,實現較為簡單。 - **自適應 Huffman** 提供靈活性但增加了實現複雜度。 - **Tunstall** 在固定碼長需求下效率高,但生成短語樹較為複雜。 - **Golomb** 對特定數據分布效率極高,實現相對簡單,特別是當 \(m\) 為 \(2^n\) 時。 ### 總結: 這四種編碼方法各有優缺點,適用於不同的應用場景和數據類型。選擇合適的編碼方法需根據具體需求、數據特性以及實現的複雜度進行權衡: - **Huffman 編碼** 是通用的無損壓縮方法,適合大多數需要高壓縮效率的場景。 - **自適應 Huffman 編碼** 適合於數據流或頻率未知的動態環境,提供即時壓縮能力。 - **Tunstall 編碼** 適合於需要固定碼長的應用,如硬件實現和並行處理環境。 - **Golomb 編碼** 在處理幾何分布或稀疏分布數據時表現最佳,適用於運行長度編碼和無線通信等場景。