算術編碼wiki簡介http://zh.wikipedia.org/wiki/%E7%AE%97%E6%9C%AF%E7%BC%96%E7%A0%81
具體做法的步驟我在網上找到個很不錯的PPT
這裡也放上來
用算術編碼方法是將被編碼的一個訊息或一個符號串(序列)表示成0和1之間的一個間隔,即對一串符號直接編碼成[0,1)區間上的一個浮點小數,在傳輸任何符號串(訊息)之前,設符號串的完整範圍為[0,1)。當一個符號被處理時,這一範圍就依據分配給這一符號的那一範圍變窄,間隔變小,當符號串序列越長,則編碼錶示它的間隔越小,同時表示這一間隔所需的位元就越多,直到完成對所有符號串的編碼。算術編碼的過程,實際上就是依據資訊源符號串的發生機率對碼區間分割的過程。
編碼過程
假如要對有10個符號的資訊源發出的字串“state tree”進行編碼,符號串具有如下的機率分布。
按每個符號出現的機率對[0,1)區間進行劃分,顯然每個符號都有一對應的子區間,這裡所用的10個字元被分配的範圍 所示。
按對‘state stree”’的算術編碼過程為:
(1)初始化時,被分割的範圍 range=high-low=[0,1) = 1 - 0 = 1,下一個範圍的低、高端分別由下式計算:
low=low+range×range low
high=low+range×range high
其中等號右邊的low為上一個被編碼字元的範圍低值;range low和range high分別為本次被編符號已給定出現的機率範圍的low和high。
(2)對訊息第1字元s編碼:s的range low=0.60,s的range high=0.70,因此下一個區間的low和high為:
low=low+range×range low=0+1×0.6=0.6
high=low+range×range high=0+1×0.7=0.7
range=high一low=0.7一0.6=0.1
S將區間[0,1) [0.6,0.7)
注意:字元“”表示“分割為”字元。
(3)對第2個字元t編碼,使用的新產生範圍為[0.6,0.7),因為t的range low=0.70,range high=1.00,因此下一個low,high分別為:
low=low+range×range low=0.6+0.1×0.7=0.67
high=low+range×range high=0.6+0.1×1.0=0.70
range=high一low=0.7一0.67=0.03
t將區間[0.6,0.7) [0.67,0.70 )
(4)對第3個字元 a編碼,在新產生的[0.67,0.70)中進行分割。因為 a的 range low=0.10,range high=0.2,因此下一個low,high分別為:
low=low+range×range low=0.67+0.03×0.1=0.673
high=low+range×range high=0.67+0.03×0.2=0.676
range=high一low=0.676一0.673=0.003
a將區間[0.67,0.7 ) [0.673,0.676 )
(5)對第4個字元t編碼,在新產生的[0.673,0.676 )上進行分割。因為 t的 range low=0.70,range high=1.00,因此下一個low,high分別為:
low=low+range×range low
=0.673+0.003×0.7=0.6751
high=low+range×range high
=0.673+0.003×1.0=0.676
range=high一low=0.676一0.6751=0.0009
t將區間[0.673,0.676 )[0.6751,0.676)
同理得到下面各字元e , ,t ,r ,e ,e 編碼所得到的範圍分別為:
[0.67528,0.67555),
[0.67528,0.675307),
[0.6752989,0.675307),
[0.67530295,0.67530376),
[0.675303112,0.675303355)
[0.6753031606,0.6753032335)。
將編碼後的區間範圍綜合:
我們用0.6753031606代表字串“state tree”,從而達到高效編碼的目的,這就是算術編碼的基本思想。
上述算術編碼區間分割過程可用表示。
解碼過程
通過編碼,最後一個子區間的的下界值0.6753031606就是字串“state tree”的惟一編碼。然後在解碼時,通過判斷哪一個字元能擁有我們已編碼的訊息落在的空間來找出訊息中的第一個字元。由於0.6753031606落在[0.6,0.7]之問,因此馬上就可解得第1個符號是S。
在解出S後,由於我們知道S的範圍的上界和下界,利用編碼的逆作用,首先除掉S的下界值0.6,得0.075303606,然後用s的範圍range=0.1去除所得到的0.0753031606,得到0.753031606,接著找出0.753031606所落在的區間[0.7,1.0)。就可解得第2個符號是t。
去掉t的下界值0.67,得0.0053031606,然後用t的range=0.03除0.0053031606,得到0.17677202,找出0.17677202所屬範圍的字元a,
如此繼續解碼操作,就可以獲得字串“state tree”的準確解碼。
簡單的類比這個過程,我取了a-j這10個字母,只編碼這麼多,每個機率假設相同都為1/10。用簡單的double型資料來儲存編碼後的結果,調試的時候發現double型太不精確了,導致了誤差很大,很失敗,後來換用java的double來寫也是一樣的,看來用這個是不行的,由於double的不準確導致連基本的編碼前的字元位元都不能確定,所以就編碼之後加上了一個位元以至於編碼後的大小不在0-1之間,不過我也只是個類比而已,所以懂得這個道理就行了。
#include<stdio.h>#include<string.h>#include<math.h>#include<malloc.h>/**編碼 (a-j)**/double BianMa(char *s){int len=strlen(s);int i;double low;//編碼第一個字元 low=(*s-'a')*0.1;//編碼剩下字元 for(i=1;i<len;i++){low=low+((*(s+i)-'a')*0.1)*pow(0.1,i);}return low+len;}/**解碼 **/char* JieMa(double s){int temp,i,len;char *str;len=(int)s;//源碼的長度s=s-len; str=(char*)malloc(sizeof(char)*len);for(i=0;i<len;i++){s=s*10;temp=(int)s;if(i==len-1) temp=temp+1;//糾正double帶來的誤差,效果不好 *(str+i)=temp+'a';s=s-temp;}*(str+i)='\0';return str;}int init(){int c;char *str;double s;str=(char*)malloc(50);printf("==============================算術編碼==============================\n");printf("僅支援編碼abcdefghij這些字元,");printf("請選擇\n1.編碼\n2.解碼\n3.退出\n");scanf("%d",&c);getchar();switch(c){case 1:printf("請輸入字元(a-j):\n");scanf("%s",str);s=BianMa(str);printf("編碼後為:%lf\n\n",s);getchar();init();break;case 2:printf("請輸入待解碼碼字:\n");scanf("%lf",&s);str=JieMa(s);printf("解碼為:%s\n\n",str);getchar();init();break;case 3:return 0;default:printf("請正確輸入\n\n");getchar();init();break;}}int main(){init();return 1;}