首頁>Club>
目前哪種壓縮演算法可以對30M左右的檔案做到較大的壓縮率,當然解壓後文件MD5值要保持不變;不考慮壓縮和解壓縮速度,請各位大佬賜教多謝(o^^o)。
4
回覆列表
  • 1 # 周林ZhouLin

    分析題主的訴求如下:

    1. 高壓縮率

    2. MD5保持不變

    對應到技術語言就是:目標壓縮演算法要滿足兩個條件:

    1. 壓縮效率高

    2. 無失真壓縮

    1. 什麼是無失真壓縮?

    無失真壓縮是指資料經過壓縮後,資訊不受損失,還能完全恢復到壓縮前的原樣。

    無失真壓縮技術主要包含兩個步驟:

    (1)建模

    (2)根據模型將出現頻率較高的部分用短碼錶示,出現頻率低的用長碼錶示。

    常見的用於步驟(2)的編碼技術有:霍夫曼編碼(也用於DEFLATE)和算術編碼。其中算術編碼可以達到進階理論最大壓縮率的上限。它之所以比霍夫曼編碼的壓縮率高是因為:霍夫曼編碼是先將輸入的資料進行分割、分組,每組資料分別編碼;而算術編碼是多組資料一起編碼。

    使用算術編碼的壓縮演算法通常先要對輸入符號的機率進行估計,然後再編碼。這個估計越準,編碼結果就越接近最優的結果。

    2. 算術編碼的演算法描述

    (1)假設有一段資料需要編碼,統計裡面所有的字元和出現的次數。

    (2)將區間 [0,1) 連續劃分成多個子區間,每個子區間代表一個上述字元, 區間的大小正比於這個字元在文中出現的機率 p。機率越大,則區間越大。所有的子區間加起來正好是 [0,1)。

    (3)編碼從一個初始區間 [0,1) 開始,設定:low = 0,high = 1

    (4)不斷讀入原始資料的字元,找到這個字元所在的區間,比如 [ L, H ),更新:

    low = low + (high - low) * L

    high = low + (high - low) * H

    (5)最後將得到的區間 [low, high)中任意一個小數以二進位制形式輸出即得到編碼的資料。

    具體例子如下:

    ARBER

    統計它們出現的次數和機率:

    將這幾個字元的區間在 [0,1) 上按照機率大小連續一字排開,我們得到一個劃分好的 [0,1)區間:開始編碼,初始區間是 [0,1)。注意這裡又用了區間這個詞,不過這個區間不同於上面代表各個字元的機率區間。這裡我們可以稱之為編碼區間,這個區間是會變化的,確切來說是不斷變小。一步一步看:

    (1)剛開始編碼區間是 [0,1),即

    low = 0high = 1

    (2)第一個字元A的機率區間是 [0,0.2),則 L = 0,H = 0.2,更新:

    low = low + (high - low)* L=0

    high = low + (high - low)* H=0.2

    (3)第二個字元R的機率區間是 [0.6,1),則 L = 0.6,H = 1,更新:

    low = low + (high - low)* L=0.12

    high = low + (high - low)* H=0.2

    (3)第三個字元B的機率區間是 [0.2,0.4),則 L = 0.2,H = 0.4,更新:

    low = low + (high - low)* L=0.136

    high = low + (high - low)* H=0.152

    (4)......

    根據上面的描述,不難看出這個演算法的特點:

    每次編碼都是在現有區間上求子區間,形成“區間套”:例如一開始A落在0到0.2上,因此編碼區間縮小為 [0,0.2),第二個字元是R,則在 [0,0.2)上按比例取出R對應的子區間 [0.12,0.2),以此類推。最後得到一個長長的小數,這個小數神奇地包含了所有的原始資料。

    3. 算術編碼的解碼過程

    就是編碼過程的逆推。從編碼得到的小數開始,不斷地尋找小數落在了哪個機率區間,就能將原來的字元一個個地找出來。例如得到的小數是0.14432,則第一個字元顯然是A,因為它落在了 [0,0.2)上,接下來再看0.14432落在了 [0,0.2)區間的哪一個相對子區間,發現是 [0.6,1), 就能找到第二個字元是R,依此類推。

    4. Java如何實現算術編碼?/** * Arithmetic Coding * */ public class Arithmetic { // Characters:{ "A", "B", "C", "D", "E", "F", "$"}} private char[] symbolchars; // P: { 0.2, 0.1, 0.2, 0.05, 0.3, 0.05, 0.1 } private double[] probability; public Arithmetic(char[] symbolchars, double[] probability) { this.symbolchars = symbolchars; this.probability = probability; } /** * @param symbols */ public double coding(String symbols) { char[] cs = symbols.toCharArray(); double symbolRangeLow[] = new double[probability.length]; for (int i = 0; i < symbolRangeLow.length; i++) { symbolRangeLow[i] = 0; for (int j = 0; j < i; j++) { symbolRangeLow[i] += probability[j]; } } int currentSymbol; double low = 0.0; double high = 1.0; double range = 1.0; for (int i = 0; i < cs.length; i++) { currentSymbol = this.getIndex(symbolchars, cs[i]); low = low + range * symbolRangeLow[currentSymbol]; high = low + range * probability[currentSymbol]; range = high - low; } return low + (high - low) / 2; } /** * * @param codeNumber */ public String deCoding(double codeNumber) { String symbols = ""; double symbolRangeLow[] = new double[probability.length]; for (int i = 0; i < symbolRangeLow.length; i++) { symbolRangeLow[i] = 0; for (int j = 0; j < i; j++) { symbolRangeLow[i] += probability[j]; } } double subSymbolRangeLow[] = new double[symbolRangeLow.length]; double subSymbolRange[] = new double[probability.length]; double subRangeLow = 0; double subRange = 0; for (int i = 0; i < symbolchars.length; i++) { subSymbolRangeLow[i] = symbolRangeLow[i]; subSymbolRange[i] = probability[i]; } int currentSymbol = 0; do { for (int i = 0; i < symbolchars.length; i++) { if (codeNumber >= subSymbolRangeLow[i] && codeNumber < subSymbolRangeLow[i] + subSymbolRange[i]) { subRangeLow = subSymbolRangeLow[i]; subRange = subSymbolRange[i]; currentSymbol = i; } } double subSymbolProbSum = subRangeLow; for (int i = 0; i < symbolchars.length; i++) { subSymbolRange[i] = subRange * probability[i]; subSymbolRangeLow[i] = subSymbolProbSum; subSymbolProbSum = subSymbolProbSum + subSymbolRange[i]; } symbols += symbolchars[currentSymbol]; } while (symbolchars[currentSymbol] != "$"); return symbols; } public int getIndex(char[] cs, char c) { for (int i = 0; i < cs.length; i++) { if (cs[i] == c) { return i; } } return -1; } //Test public static void main(String args[]) { char[] symbolchars = { "A", "B", "C", "D", "E", "F", "$" }; double[] probability = { 0.2, 0.1, 0.2, 0.05, 0.3, 0.05, 0.1 }; Arithmetic arithmetic = new Arithmetic(symbolchars, probability); double codeNumber = arithmetic.coding("CAAF"); System.out.println(codeNumber); System.out.println(arithmetic.deCoding(codeNumber)); } }

  • 中秋節和大豐收的關聯?
  • 你們身邊有自閉症的小孩嗎?他的家庭有什麼故事呢?