转 算术编码算法的分析与实现

合集下载

算数编码上机实验报告

算数编码上机实验报告

一、实验目的1. 理解算数编码的基本原理。

2. 掌握算数编码的实现方法。

3. 分析算数编码的优缺点,并与其他编码方法进行比较。

4. 提高编程能力和算法实现能力。

二、实验环境1. 操作系统:Windows 102. 编程语言:Python3.83. 编译器:Python 解释器4. 输入数据:文本文件三、实验内容本次实验主要涉及以下内容:1. 算数编码的基本原理。

2. 算数编码的算法实现。

3. 算数编码的性能分析。

四、实验步骤1. 数据预处理首先,读取输入文本文件,并将文本转换为字符序列。

然后,统计每个字符出现的频率,并按照频率从高到低进行排序。

2. 算数编码实现(1)初始化:创建一个闭区间 [0, 1],该区间表示所有可能的编码。

(2)编码过程:a. 对于待编码的字符,根据其频率将其对应的概率分配到编码区间上。

b. 计算该字符对应的概率区间 [p1, p2]。

c. 将编码区间 [0, 1] 分成 [0, p1] 和 [p1, p2] 两个子区间。

d. 根据字符对应的概率区间,选择其中一个子区间作为新的编码区间。

e. 重复步骤 b、c、d,直到编码区间缩小到只有一个字符的区间。

(3)解码过程:a. 初始化解码区间为 [0, 1]。

b. 遍历编码后的数据,根据每个数据对应的概率区间,逐步缩小解码区间。

c. 当解码区间缩小到只有一个字符的区间时,输出该字符。

3. 性能分析(1)编码长度:比较算数编码与其他编码方法(如Huffman编码、LZ77编码等)的编码长度。

(2)编码时间:比较算数编码与其他编码方法的编码时间。

(3)解码时间:比较算数编码与其他编码方法的解码时间。

五、实验结果与分析1. 编码长度通过实验结果,我们发现算数编码的编码长度明显优于Huffman编码和LZ77编码。

在相同的数据量下,算数编码的编码长度更短,有利于数据压缩。

2. 编码时间算数编码的编码时间略长于Huffman编码,但短于LZ77编码。

编码与解码算法原理与实现

编码与解码算法原理与实现

编码与解码算法原理与实现一、引言编码和解码是计算机科学中的两个重要概念。

编码是指将信息从一种形式转换为另一种形式,而解码则是将编码后的信息转换回原始形式。

在计算机领域,编码和解码算法被广泛应用于数据传输、存储以及安全等方面。

本文将详细介绍编码与解码算法的原理和实现步骤。

二、编码算法原理与实现步骤编码算法是将信息转换为另一种形式的过程。

常见的编码算法包括Base64、哈夫曼编码等。

下面以Base64编码算法为例,介绍其原理和实现步骤。

1. 原理Base64编码算法是一种用64个字符来表示任意二进制数据的方法。

它将原始信息分割成固定长度的块,并将每个块转换为对应的Base64字符。

转换的过程包括以下步骤:- 将原始信息转换为二进制数据;- 对二进制数据进行分割,每个分割后的块长度为24位,不足24位的在末尾补0;- 对每个24位块进行转换,将其分割为4个6位的块;- 将每个6位块转换为对应的Base64字符;- 将转换后的Base64字符拼接起来,即为编码后的结果。

2. 实现步骤Base64编码算法的实现可以分为以下几个步骤:- 将原始信息转换为二进制数据:首先,将原始信息转换为ASCII码表示的字符;然后,将每个字符转换为对应的二进制数据;- 对二进制数据进行分割,每个分割后的块长度为24位:将二进制数据按照每24位进行分割,并在末尾补0;- 对每个24位块进行转换,将其分割为4个6位的块:将每个24位块拆分为4个6位的块,保存起来备用;- 将每个6位块转换为对应的Base64字符:将每个6位的块转换为对应的Base64字符;- 将转换后的Base64字符拼接起来,即为编码后的结果:将转换后的Base64字符按照顺序拼接起来,即可得到编码后的结果。

三、解码算法原理与实现步骤解码算法是将编码后的信息转换回原始形式的过程。

下面以Base64解码算法为例,介绍其原理和实现步骤。

1. 原理Base64解码算法是将Base64编码后的信息转换回原始形式的方法。

算数编码的原理

算数编码的原理

算数编码的原理
算术编码是一种无损数据压缩算法,它通过将整个数据序列映射到一个连续的数值区间来实现压缩。

算术编码的原理可以概括为以下几个步骤:
确定符号集:确定待编码的符号集,这可以是字符、像素值或其他离散符号的集合。

计算符号概率:对于每个符号,计算其在待编码数据中出现的概率。

通常使用统计方法或概率模型进行估计。

构建累积概率表:根据符号概率计算符号的累积概率。

累积概率表示为每个符号之前的概率总和。

映射到区间:将待编码数据序列中的每个符号映射到一个区间,该区间是 [0, 1) 上的一个子区间。

初始区间为整个区间 [0, 1)。

缩小区间:根据每个符号的累积概率表和当前区间,将当前区间缩小为表示下一个符号的子区间。

缩小区间的过程可以通过二分搜索或线性插值来实现。

重复步骤 5:对于待编码数据序列中的每个符号,重复步骤 5,不断缩小当前区间。

输出编码结果:最终,将最后一个符号所对应的子区间输出为编码结果。

这个子区间可以用一个二进制码或其他形式的码字来表示。

解码过程与编码过程相反,它将编码结果映射回原始数据序列。

解码过程需要使用与编码过程相同的符号概率和累积概率表。

算术编码的优势在于它可以实现较高的压缩比,因为它能够有效地利用符号的概率信息。

然而,算术编码的实现相对复杂,需要对概率进行准确的估计,并且在解码过程中需要高精度的计算。

算术编解码实验报告

算术编解码实验报告

一、实验目的1.进一步学习C++语言概念和熟悉VC 编程环境。

2.学习算术编码基本流程, 学会调试算术编码程序。

3. 根据给出资料,自学自适应0 阶算术编、解码方法。

二、实验要求:(1)实验前编写源程序、准备测试数据。

(2)在Turbo C下完成程序的编辑、编译、运行,获得程序结果。

如果结果有误,应找出原因,并设法更正之。

三、实验内容编程实现任给n个概率和为1的随机分布字符,对任意排列的字符序列进行算术编码,计算平均码长。

得到编码结果并进行解码#include<iostream.h>#include"math.h" //定义所需要用到的变量及数组char S[100], A[10];float P[10],f[10],gFs;//编码程序void bianma(int a,int h){ int i,j;float fr;float ps=1;float Fs=0;float Sp[100],b[100],F[100]; //以待编码的个数和字符个数为循环周期,将待编码的字符所对应的概率存入到Fs中for(i=0;i<h;i++){ for(j=0;j<a;j++){ if(S[i]==A[j]){ Sp[i]=P[j];fr=f[j];//将划分好的[0,1)区间的对应点赋值给fr}}Fs=Fs+ps*fr;//从选择的子区间中继续进行下一轮的分割。

不断的进行这个过程,直到所有符号编码完毕。

ps*=Sp[i]; //求Ps}cout<<"Fs="<<Fs<<endl;//显示最终的算术编码gFs=Fs;float l=log(1/ps)/log(2);//计算算术编码的码字长度lif(l>(int)l)l=(int)l+1;else l=int(l); //将Fs转换成二进制的形式int d[20];int m=0;while(l>m){ Fs=2*Fs;if(Fs>1){ Fs=Fs-1;d[m]=1;}else if(Fs<1)d[m]=0;else {d[m]=1;break;}m++;}int z=m;//解决有关算术编码的进位问题,给二进制数加1if(m>=l){ while(1){ d[m-1]=(d[m-1]+1)%2;//最后位加1if(d[m-1]==1)break;else m--;}}cout<<"s=";for(int e=0;e<z;e++)cout<<d[e];cout<<endl;}//解码程序void jiema(int a,int h){ int i,j;float Ft,Pt;float Fs=0,Ps=1;for(i=0;i<h;i++)//以编码个数和符号个数为循环周期,对其进行解码 { for(int j=a-1;j>-1;j--){ Ft=Fs;Pt=Ps;Ft+=Pt*f[j];//对进行逆编码Pt*=P[j];if(gFs>=Ft)//对其进行判断,并且将值存入到数组A中{ Fs=Ft;Ps=Pt;cout<<A[j];break;}}}cout<<endl;}void main(){ cout<<"输入所要编码的符号的个数,并按回车跳转:"<<endl;int a,i,h=0;cin>>a;cout<<"请输入符号及其相对应的概率值,并按回车跳转:"<<endl;for(i=0;i<a;i++){ char x;float y;cin>>x;A[i]=x;//将字符依次存入数组A中cin>>y;P[i]=y;//将字符所对应的概率依次存入到数组P中}for(i=1;i<a;i++){ f[0]=0;f[i]=f[i-1]+P[i-1];//将要编码的数据映射到一个位于[0,1)的实数区间中}cout<<"请输入所要编码的符号序列,并以*结尾:"<<endl;while(1)//这个while语句的作用是将要编码的字符存入到数组S中{ char ss;cin>>ss;if(ss=='*')break;//在以“*”为结尾的时候结束存储S[h++]=ss;}cout<<"输入的字符经过算术编码之后为:"<<endl;bianma(a,h);cout<<"由上述所对应的解码为:"<<endl;jiema(a,h);}四、实验数据记录及分析实验取a、b、c、d四个字符,概率分别为0.4、0.3、0.2、0.1;对字符序列abcdadb进行算术编码。

算术编码的原理

算术编码的原理

算术编码的原理算术编码是一种数据压缩算法,它可以将一个长字符串压缩成一个更短的数值。

它与其他数据压缩算法不同,它不是将整个字符串划分成固定长度的块,而是将每个字符映射为一个数字,再将这些数字压缩成一个数值。

算术编码的原理可以简单地概括为以下几点:1. 确定字符集在压缩之前,必须先确定字符集。

字符集包括所有可能出现的字符。

例如,在英语中,字符集包括所有字母、数字以及其他符号。

2. 计算每个字符的概率通过预处理或对大量数据的统计,可以计算每个字符在字符串中出现的概率。

3. 对每个字符进行编码编码的过程是将每个字符映射为一个数字。

这个数字必须能唯一地表示每个字符,并且尽可能不会出现冲突。

编码的方式可以根据具体情况进行选择,例如 ASCII 码就是一种常见的字符编码方式。

4. 计算每个字符的编码区间每个字符根据其在字符串中出现的概率,可以确定一个编码区间。

例如,一个字符在字符串中出现的概率为 0.25,则其编码区间为 0-0.25。

5. 压缩数据将每个字符的编码区间连续地组成一个区间,最终压缩成一个数值。

如果字符集很大,压缩后得到的数值可能非常大,因此需要使用高精度运算来处理。

6. 解压数据解压数据的过程就是将压缩后的数值还原为原始字符串的过程。

解压的过程需要根据先前编码的字符集和编码区间进行计算,从而还原字符串。

总之,算术编码的原理可以简单概括为确定字符集、计算每个字符的概率、为每个字符编码、计算每个字符的编码区间、压缩数据和解压数据。

虽然算术编码的实现比较复杂,但它可以很好地压缩数据,并且是一种通用的数据压缩算法。

算术编码的发展和研究热点

算术编码的发展和研究热点

算术编码的发展和研究热点一、简单介绍。

哈夫曼编码是一种常用的数据压缩编码方法,是哈夫曼于1952年为压缩文本文件提出的一种变长编码方法。

它的基本思想是对出现概率大的符号分配短字长的二进制代码,对出现概率小的符号则分配长字长的二进制代码,从而最终得到每个符号码字平均码长最短的码。

因此对每一个输入符号都对应一个码字,而且该输入符号的出现概率必须是2的整数次幂,对应的码字字长必须是整数比特。

算术编码(ARITHMETIC CODING)是利用信源统计特性对码率进行无损压缩的一种编码方式,属于熵编码范畴,实现算术编码首先需要知道信源发出每个符号的概率大小,然后再扫描符号序列,依次分割相应的区间,最终得到符号序列所对应的码字。

整个编码需要两个过程,即概率模型建立过程和扫描编码过程。

其编码的目的是为了最大程度的降低符号间冗余度,从而获得尽可能高的压缩效率。

早在上个世纪60年代,Abralnson就根据信息论中信源序列积累概率的概念提出了算术编码的基本思想,虽然它的编码效率非常理想,接近于最佳的编码效率,但由于计算精度的问题,直到70年代中期算术编码一直停留在理论阶段。

此后RubinGuazsanen和Langd等人对算术编码做出了不断改进,算术编码进入实用阶段,1987年IanH.witen等人公布了现代多符号算术编码的第一个纯软件版本,算术编码才逐渐得以比较广泛应用。

目前在不少的数据压缩领域都在应用算术编码,例如英文文件编码(领域)、活动图像和目前最新的H264视频压缩编译码标准和静止图像(JPEG压缩标准) 压缩编码领域。

下面简要的说明其编码原理,并与目前广泛应用的huffman编码做全面的比较,以此来说明算术编码的先进性,优越性。

算术编码的基本思想是用一个特定的代码来代替整串输入符号,并不为每个输入符号都产生一个单独的代码,而是为整个一条消息产生一个代码。

任何增加到消息中的每一个符号,都会提高代表该消息的浮点数值精度,这就需要更多的比特数来记录该浮点数值。

python算术编码

python算术编码## 简介这篇原创文档将会介绍算术编码的基本原理和实现。

算术编码是一种无损数据压缩算法,在信息论领域得到广泛应用。

通过使用算术编码,可以将数据压缩成更短的编码序列,从而减少存储空间的需求。

## 基本原理算术编码的基本原理是将输入序列映射到一个实数区间中,并将该区间的特定部分表示为输出序列。

其实质是将较常见的序列映射为较短的编码,而较不常见的序列映射为较长的编码。

通过这种方式,可以实现数据的高度压缩。

具体而言,算术编码的过程如下:1. 将输入序列划分为不同的符号,并为每个符号分配一个概率。

2. 计算每个符号出现的概率区间(其实也可以是累积概率)。

3. 将整个区间根据各个符号的概率进行划分,并将输入序列映射到对应的子区间中。

4. 重复步骤3,直到得到一个唯一的编码序列。

## 实现步骤下面将介绍算术编码的实现步骤。

1. 初始化区间:将整个区间初始设置为[0, 1)。

2. 计算符号概率区间:针对每个输入符号,根据其出现的概率计算对应的区间。

可以通过累积概率或离散概率来计算。

3. 映射到区间:根据符号概率区间,将输入序列映射到对应的子区间中。

例如,如果输入序列为"ABBC",对应的符号概率区间为[0.2, 0.4),则将当前区间更新为子区间[0.2, 0.3)。

4. 缩小区间:重复步骤3,不断缩小区间,直到得到一个唯一的编码序列。

5. 输出编码序列:将最终的区间映射到一个编码序列中,并输出。

## 优缺点算术编码的优点在于其高度压缩的能力,能够将数据压缩到接近信息熵的程度。

另外,算术编码对于任意概率分布的符号集都能进行有效的编码。

然而,算术编码也存在一定的缺点。

首先,算术编码的实现相对复杂,需要进行精确的浮点数计算。

其次,算术编码对输入数据的顺序敏感,即输入序列的顺序不同,得到的编码序列也会不同。

## 总结算术编码是一种强大的数据压缩算法,其利用符号出现的概率将输入序列映射到一个实数区间中,并通过缩小区间来得到一个唯一的编码序列。

python算术编码

python算术编码算术编码是一种常用的无损压缩算法,可以对数据进行高效的编码和解码,以达到数据压缩的目的。

本文将介绍算术编码的原理和实现,以及其在实际应用中的一些注意事项。

1. 算术编码原理算术编码将数据编码为一个区间,该区间表示数据的概率分布。

编码过程中,每个符号根据其出现的概率被分配一个子区间。

编码最后输出的是包含所有符号的区间的编码值。

解码过程则是根据编码值将其映射回原始数据。

2. 算术编码步骤算术编码主要包含以下几个步骤:- 确定每个符号的概率分布。

- 计算每个符号所对应的区间。

- 编码:根据符号和区间,将数据编码为一个编码值。

- 解码:根据编码值和符号的概率分布,将编码值解码为原始数据。

3. 算术编码的实现算术编码的实现需要对概率进行建模,并进行区间的计算。

下面是一个简单的算术编码的实现示例:```pythondef arithmetic_encode(data, symbol_list, probability_list):low = 0high = 1result = 0for symbol in data:symbol_index = symbol_list.index(symbol)symbol_low = low + (high - low) *sum(probability_list[:symbol_index])symbol_high = low + (high - low) *sum(probability_list[:symbol_index + 1])low = symbol_lowhigh = symbol_highresult = (low + high) / 2return resultdef arithmetic_decode(encoded_data, symbol_list, probability_list, length):low = 0high = 1result = []for _ in range(length):for i in range(len(symbol_list)):symbol_low = low + (high - low) * sum(probability_list[:i]) symbol_high = low + (high - low) * sum(probability_list[:i + 1])if symbol_low <= encoded_data < symbol_high:result.append(symbol_list[i])low = symbol_lowhigh = symbol_highbreakreturn result```以上代码中,`arithmetic_encode`函数用于将数据编码为编码值,`arithmetic_decode`函数用于将编码值解码为原始数据。

算术编码的基本原理

算术编码的基本原理算术编码是一种数据压缩和信息理论技术,用于将一个消息序列转换为一个紧凑的二进制编码。

它广泛应用于无损压缩算法中。

算术编码的基本原理是将每个符号映射到一个小数区间,然后根据这些区间的重叠来表示整个消息序列。

这个区间由两个数字表示,称为低值和高值。

初始时,低值为0,高值为1。

然后,将消息中的第一个符号映射到一个子区间,该子区间与原始区间重叠。

这样的重叠会产生精度损失,因此需要反复迭代此过程以提高编码效率。

算术编码的具体步骤如下:1. 确定符号集合:需要将消息编码为一系列符号。

符号可以是单个字符,也可以是由多个字符组成的字符串。

符号集合需要包括所有可能出现的符号。

2. 确定符号频率:对于每个符号,需要确定其在消息中出现的频率。

频率可以通过统计消息序列中每个符号的出现次数来获得。

3. 计算累积概率:对于每个符号,计算其累积概率。

累积概率是指该符号及其之前的所有符号出现的概率之和。

4. 划定区间:对于每个符号,根据其累积概率将区间划分为子区间。

每个符号对应的子区间的长度与其概率成比例。

5. 重新调整区间:由于划定的子区间与原始区间可能有重叠,需要对区间进行调整。

一种常见的方法是将区间乘以概率,使其适应于新的子区间。

6. 缩小区间:对于每个新的符号,重复以上过程,缩小当前区间范围。

7. 输出编码:对于最后一个符号,输出编码位于区间内的任意一点,这个点表示整个消息序列的二进制编码。

算术编码是一种非常灵活的压缩技术,因为它可以根据不同的符号频率分配不同长度的编码。

频率越高的符号将被分配较短的编码,从而实现更高的压缩率。

然而,算术编码也存在一些限制,比如需要大量的计算和存储资源,并且在传输过程中对于传输错误非常敏感。

尽管如此,算术编码在很多应用中得到了广泛的应用,比如无损图像和音频压缩中的JPEG和MP3算法。

它可以实现更高的压缩率,同时保持数据的完整性,是一种理论上更为高效的数据压缩方法。

算术码实验报告

实验名称:算术码编码与解码实验实验目的:1. 理解算术码的基本原理和编码方法。

2. 掌握算术码的编码和解码过程。

3. 评估算术码在数据压缩和传输中的应用效果。

实验时间:2023年X月X日实验地点:计算机实验室实验设备:计算机、实验软件实验人员:XXX、XXX、XXX实验内容:一、实验原理算术码是一种无损失的数据压缩编码方法,它将数据信息映射到某个区间内的一个概率分布上,然后用该概率分布对数据进行编码。

算术码的优点是压缩效果好,抗干扰能力强,但计算复杂度较高。

二、实验步骤1. 数据准备选择一组实验数据,如文本文件、图片等,用于进行算术码编码和解码实验。

2. 编码过程(1)计算数据中每个符号出现的概率。

(2)将数据映射到[0,1)区间内的概率分布上。

(3)根据概率分布对数据进行编码,生成算术码。

3. 解码过程(1)读取算术码。

(2)根据算术码计算每个符号出现的概率。

(3)将概率分布映射回原始数据。

4. 评估效果比较编码前后数据的长度,计算压缩比;同时,对比原始数据和解码后的数据,检查解码的正确性。

三、实验结果与分析1. 编码过程以文本文件为例,计算每个字符出现的概率,得到概率分布。

然后将文本文件映射到[0,1)区间内的概率分布上,生成算术码。

2. 解码过程读取算术码,根据概率分布计算每个字符出现的概率,将概率分布映射回原始数据。

3. 评估效果(1)压缩比:编码前文本文件长度为X,编码后长度为Y,压缩比为Y/X。

(2)解码正确性:解码后的数据与原始数据完全一致。

实验结果表明,算术码在数据压缩和传输中具有较好的效果。

在实际应用中,可以根据具体需求调整编码和解码过程,以达到更好的压缩效果。

四、实验总结1. 算术码是一种有效的数据压缩编码方法,具有较好的压缩效果和抗干扰能力。

2. 通过实验,掌握了算术码的编码和解码过程,为实际应用提供了理论基础。

3. 实验结果表明,算术码在数据压缩和传输中具有较好的效果,为相关领域的研究提供了参考。

  1. 1、下载文档前请自行甄别文档内容的完整性,平台不提供额外的编辑、内容补充、找答案等附加服务。
  2. 2、"仅部分预览"的文档,不可在线预览部分如存在完整性等问题,可反馈申请退款(可完整预览的文档不适用该条件!)。
  3. 3、如文档侵犯您的权益,请联系客服反馈,我们会尽快为您处理(人工客服工作时间:9:00-18:30)。

转算术编码算法的分析与实现[转]算术编码算法的分析与实现2011-06-09 14:20本论文题目:算术编码算法的分析与实现,作者:叶叶,于2010年10月16日在编程论坛上发表。

页面地址:。

本论文全文及相关配套程序可以在上述页面中下载。

请尊重他人劳动成果,转载或引用时请注明出处。

目录1前言2 2理论2 2.1编码2 2.2解码3 3改进4 3.1整数运算4 3.2正规化5 4实现8 4.1编码8 4.2解码10 4.3统计模型11 5分析12 6结束语12参考文献13附录13算术编码算法的分析与实现作者:叶叶(网名:yeye55)摘要:分析了算术编码的理论基础,着重介绍WNC算法的实现方式。

详细讨论了算术编码原理、正规化操作、WNC算法代码实现等技术。

给出了一个切实可行的应用程序。

关键词:算术编码;正规化;Delphi中图分类号:TP301.6 1前言早在1948年C.E.Shannon提出信息论[1]的时候,就提出了算术编码的思想。

但是经过多年的研究,许多学者认为算术编码是无法实现的。

算术编码要求进行无限精度的实数运算,这在仅能进行有限精度运算的计算机系统上是无法进行的。

随着研究的深入,终于在1987年Ian H.Witten、Radford M.Neal和John G.Cleary发表了一篇论文[2],提出了一种基于整数运算的算术编码实现算法。

该算法后来被命名为CACM87,并应用于ITU-T的H.236视频编码标准。

也有学者根据作者姓名将该算法称之为WNC算法。

WNC算法是一个实用性算法,它可以应用在许多方面。

在Witten等人的论文[2]中给出了一个使用C语言编写的WNC算法实现程序的源代码(以下简称"WNC源代码")。

在许多时候,WNC源代码已经作为算术编码的范本程序来使用。

本文将分析算术编码的理论基础,并着重介绍WNC算法的实现方式。

同时给出一个在Delphi 7.0下开发,使用算术编码算法压缩数据的应用程序。

2理论2.1编码算术编码将整个要编码的数据映射到一个位于[0,1)的实数区间中。

并且输出一个小于1同时大于0的小数来表示全部数据。

利用这种方法算术编码可以让压缩率无限的接近数据的熵值,从而获得理论上的最高压缩率。

算术编码进行编码时,从实数区间[0,1)开始。

按照符号的频度将当前的区间分割成多个子区间。

根据当前输入的符号选择对应的子区间,然后从选择的子区间中继续进行下一轮的分割。

不断的进行这个过程,直到所有符号编码完毕。

对于最后选择的一个子区间,输出属于该区间的一个小数。

这个小数就是所有数据的编码。

现在来举个例子。

假设一份数据由"A"、"B"、"C"三个符号组成。

现在要编码数据"BCCB",编码过程如图2.1所示。

图2.1"BCCB"的编码过程首先说明一点,这里使用的是自适应模型。

也就是说一开始时,三个符号的频度都是1。

随着编码的进行再更新频度。

另外,在计算时理论上要使用无限小数。

这里为了说明方便,四舍五入到小数点后4位。

观察图2.1可以发现算术编码的过程。

首先,算术编码是从区间[0,1)开始的。

这时三个符号的概率都是1/3,按照这个概率分割区间。

第一个输入的符号是"B",所以我们选择子区间[0.3333,0.6667)作为下一个区间。

输入"B"后更新频度,根据新的概率对区间[0.3333,0.6667)进行分割。

这时输入的符号是"C",我们可以选择子区间[0.5834,0.6667)。

继续更新频度、分割区间、选择子区间,直到符号全部编码完成。

我们最后得到的区间是[0.6390,0.6501)。

输出属于这个区间的一个小数,例如0.64。

那么经过算术编码的压缩,数据"BCCB"最后输出的编码就是0.64。

2.2解码算术编码进行解码时仅输入一个小数。

解码前首先需要对区间[0,1)按照初始时的符号频度进行分割。

然后观察输入的小数位于那个子区间。

输出对应的符号,选择对应的子区间,然后从选择的子区间中继续进行下一轮的分割。

不断的进行这个过程,直到所有的符号都解码出来。

整个过程相当于编码时的逆运算。

在我们的例子中,输入的小数是0.64。

首先,初始时三个符号的概率都是1/3,按照这个概率分割区间。

观察图2.1可以发现0.64落在子区间[0.3333,0.6667)中,于是可以解码出"B"。

并且选择子区间[0.3333,0.6667)作为下一个区间。

输出"B"后更新频度,根据新的概率对区间[0.3333,0.6667)进行分割。

这时0.64落在子区间[0.5834,0.6667)中,于是可以解码出"C"。

按照上述过程进行,直到所有的符号都解码出来。

可见,只需要一个小数就可以完整还原出原来的所有数据。

3改进3.1整数运算上一节中描述的算法,在当前的计算机系统上是很难实现的。

尤其是无限精度的实数运算。

所以在实现的时候,需要对算法做一些改进。

使得它可以在当前的计算机系统上较快的运行。

当然,这种改进是以降低运算精度为代价的。

也就是说,这种改进实际上会降低算法的压缩率。

但是,它会使算法的实现成为可能。

观察前面描述的算法过程可以发现,运算时区间的上下沿都是小于1的小数。

那么我们可以省略0和小数点,仅仅使用小数的尾数来表示小数。

省略0和小数点后的尾数,实际上就是一个无限大的整数。

使用无限整数的部分高位来表示整数,并在这些整数上进行整数运算就可以模拟出实数运算。

在我们的例子里,可以使用区间[3333,6667)来表示区间[0.3333,0.6667)。

最后可以输出64来表示0.64。

另外,分割区间、选择子区间的过程,相当于将一个区间映射到另一个更小的区间中(以下简称"映射区间")。

如果我们知道一个符号的频度。

以及符号值小于该符号的其它符号的频度总计(以下简称"累积频度(Cumulative Frequency)")。

还有到目前为止所有符号频度的总计(以下简称"总计频度(Total Frequency)")。

那么就可以根据这些频度信息,从当前区间中计算出映射区间。

计算的公式如下。

Range=High-Low+1 High=Low+Range*(CumFreq+Freq)div Total-1Low=Low+Range*CumFreq div Total其中Low表示区间的下沿;High表示区间的上沿;Range表示区间的范围;Freq 表示符号频度;CumFreq表示累积频度;Total表示总计频度。

这些变量中保存的都是整数,并进行整数运算。

其中div表示整除。

另外需要注意一点,这里使用闭区间[Low,High],而不是使用右开区间[Low,High)。

在我们的例子里,实数运算时四舍五入到小数点后4位。

那么在整数运算时可以采用4位整数来进行。

初始区间可以设定在[0,9999]的闭区间中。

按照上述公式进行编码计算所得的结果如表3.1所示。

输入数据输入符号映射区间区间范围""[0000,9999]10000"B"B[3333,6665]3333"BC"C[5832,6665]834"BCC"C[6332,6665]334"BCCB"B[6387,6498]112表3.1整数运算的区间变化将表3.1中的数据与图2.1中的数据进行对比可以发现,整数运算会降低运算精度。

整数运算时最后映射到区间[6387,6498],实数运算时最后映射到区间[0.6390,0.6501)。

由于精度降低运算出现了误差,但是我们仍旧可以输出64代替0.64来表示整个数据。

所以这种精度的降低是在允许的范围内。

在解码的时候也可以进行整数运算。

根据输入的整数数值、当前区间的下沿和总计频度,可以计算出一个估算出来的累积频度(以下简称"估算频度(Estimate Frequency)")。

其计算公式如下。

Range=High-Low+1 EstFreq=((Value-Low+1)*Total-1)div Range其中,Value表示输入的整数数值;EstFreq表示估算频度。

利用估算频度在当前的累积频度表中查找,当满足CumFreq≤EstFreq<CumFreq+Freq的条件时,就可以解码出一个符号。

利用解码出的符号可以得到对应的累积频度和频度。

根据这些频度信息,可以从当前区间中计算出映射区间。

这一点同编码时是一样的。

计算出映射区间后,更新对应符号的频度,又可以进行新的一轮解码。

在我们的例子中,输入的整数数值是64。

但是64本质上是0.64,所以在参与运算时要将64扩展成6400。

初始时区间的范围同编码时是一样的,从[0,9999]开始。

利用6400进行解码,其过程如表3.2所示。

映射区间估算频度累积频度解码符号A BC[0000,9999]1 01 2B[3333,6665]3 01 3C[5832,6665]3 01 3C[6332,6665]1 01 3B表3.2整数运算的解码过程可以看出利用一个整数数值64,就可以解码出全部数据。

另外,观察解码过程可以发现。

在解码时不仅要计算映射区间,还要计算和查找估算频度。

所以算术编码的解码过程通常要比编码过程慢。

在本小节中给出的计算公式都来自WNC源代码。

观察这些计算公式可以发现,有许多运算是重复的。

这意味着,这些公式还有改进的可能。

在本文的第4节中将给出改进后的计算方法。

3.2正规化上述算法实际上是无法实现的。

观察表3.1可以发现,随着编码的进行区间范围会越来越小,最后区间范围会趋向0。

如果编码较长的数据,区间范围为0时就无法继续编码。

解决这一问题的方法是使用正规化(Renormalization,又称"归一化")。

正规化操作就是当区间的上下沿满足一定的条件时,将一定的位数从区间中移出,同时对区间进行一次放大。

使用正规化操作,可以在有限区间上模拟无限区间的运算。

当然这种模拟同样会降低精度,但是它让无限区间的运算成为可能。

下面就来介绍正规化操作的过程。

上一节已经说过,区间的运算可以转换为整数运算。

而区间的上下沿都是用整数来保存。

在实现的时候,都是进行二进制整数运算。

在本节中为了说明方便全部使用二进制整数来表示区间的上下沿。

那么对于一个区间的上下沿有可能出现以下两中情况。

[00101101,或[10101101,01001011]11001011]情况1情况2可以发现这两种情况中,区间上下沿的最高位都是相同的。

相关文档
最新文档