算术编码的C实现
- 格式:doc
- 大小:23.50 KB
- 文档页数:5
实现算术编码及其译码一、实验内容借助C++编程来实现对算术编码的编码及其译码算法的实现二、实验环境1.计算机2.VC++6.0三、实验目的1.进一步熟悉算术编码的原理,及其基本的算法;2.通过编译,充分对于算术编码有进一步的了解和掌握;3.掌握C++语言编程(尤其是数值的进制转换,数值与字符串之间的转换等)四、实验原理算术编码算术编码的基本原理是将编码的消息表示成实数0和1之间的一个间隔,消息越长,编码表示它的间隔就越小,表示这一间隔所需的二进制位就越多。
算术编码用到两个基本的参数:符号的概率和它的编码间隔。
信源符号的概率决定压缩编码的效率,也决定编码过程中信源符号的间隔,而这些间隔包含在0到1之间。
编码过程中的间隔决定了符号压缩后的输出。
给定事件序列的算术编码步骤如下:(1)编码器在开始时将“当前间隔”设置为[0,1)。
(2)对每一事件,编码器按步骤(a)和(b)进行处理(a)编码器将“当前间隔”分为子间隔,每一个事件一个。
(b)一个子间隔的大小与下一个将出现的事件的概率成比例,编码器选择子间隔对应于下一个确切发生的事件相对应,并使它成为新的“当前间隔”。
(3)最后输出的“当前间隔”的下边界就是该给定事件序列的算术编码。
编码过程假设信源符号为{A, B, C, D},这些符号的概率分别为{ 0.1, 0.4, 0.2,0.3 },根据这些概率可把间隔[0, 1]分成4个子间隔:[0, 0.1], [0.1, 0.5],[0.5, 0.7], [0.7, 1],其中[x,y]表示半开放间隔,即包含x不包含y。
上面的信息可综合在表03-04-1中。
下表为信源符号,概率和初始编码间隔如果二进制消息序列的输入为:C A D A C D B。
编码时首先输入的符号是C,找到它的编码范围是[0.5,0.7]。
由于消息中第二个符号A的编码范围是[0,0.1],因此它的间隔就取[0.5, 0.7]的第一个十分之一作为新间隔[0.5,0.52]。
自适应算术编码的原理实现与应用简介自适应算术编码(Adaptive Arithmetic Coding)是一种无损数据压缩算法,用于将输入数据流转换为更短的编码表示形式。
相对于固定长度编码,自适应算术编码能够更好地利用数据的统计特性,从而达到更高的压缩比。
本文将介绍自适应算术编码的原理实现与应用,并对其进行详细的解释与示例。
原理自适应算术编码的原理非常简单,主要分为以下几个步骤:1.定义符号表:首先,需要将输入数据中的符号进行编码,因此需要定义一个符号表,其中包含了所有可能的符号及其概率。
符号可以是字符、像素、或者任意其他离散的数据单元。
2.计算累积概率:根据符号表中每个符号的概率,计算出累积概率。
累积概率用于将输入数据中的符号映射到一个区间上。
3.区间编码:将输入数据中的符号通过区间编码进行压缩。
每个符号对应一个区间,区间的大小与符号的概率成比例。
4.更新概率模型:在每次编码过程中,根据已经编码的符号,可以得到新的概率模型。
根据这个模型,可以动态地调整符号表中每个符号的概率。
这样,在下一次编码中,就能更好地适应数据的统计特性。
实现步骤与示例1.定义符号表假设我们要对一个字符串进行压缩,其中包含的符号为’a’、’b’、’c’、’d’和’e’。
我们可以根据经验或者统计数据,估计每个符号的概率。
例如:’a’的概率为0.2,’b’的概率为0.15,’c’的概率为0.3,’d’的概率为0.25,’e’的概率为0.1。
2.计算累积概率根据符号表中每个符号的概率,计算出累积概率。
累积概率可以通过累计每个符号的概率得到。
在本示例中,累积概率为:’a’的概率为0.2,’b’的概率为0.35,’c’的概率为0.65,’d’的概率为0.9,’e’的概率为1.0。
3.区间编码使用累积概率对输入数据中的符号进行区间编码。
假设我们要对字符串’abecd’进行编码。
–第一个符号为’a’,其累积概率为0.2。
因此,我们将区间[0,1.0)划分为5个小区间,每个小区间对应一个符号:•’a’对应的区间为[0,0.2);•’b’对应的区间为[0.2,0.35);•’c’对应的区间为[0.35,0.65);•’d’对应的区间为[0.65,0.9);•’e’对应的区间为[0.9,1.0)。
实验三算术编码一、实验目的1.进一步学习C++语言概念和熟悉VC 编程环境。
2.学习算术编码基本流程, 学会调试算术编码程序。
3. 根据给出资料,自学自适应0 阶算术编、解码方法。
二、实验内容与原理(一)实验原理:1.算术编码基本原理这是将编码消息表示成实数0 和1 之间的一个间隔,消息越长,编码表示它的间隔就越小,表示这一间隔所需的二进制位就越多。
算术编码用到两个基本的参数:符号的概率和它的编码间隔。
信源符号的概率决定压缩编码的效率,也决定编码过程中信源符号的间隔,而这些间隔包含在0到1之间。
编码过程中的间隔决定了符号压缩后的输出。
首先借助下面一个简单的例子来阐释算术编码的基本原理。
考虑某条信息中可能出现的字符仅有a b c 三种,我们要压缩保存的信息为bccb。
在没有开始压缩进程之前,假设对a b c 三者在信息中的出现概率一无所知(采用的是自适应模型),暂认为三者的出现概率相等各为1/3,将0 - 1 区间按照概率的比例分配给三个字符,即a 从0.0000 到0.3333,b 从0.3333 到0.6667,c 从0.6667 到1.0000。
进行第一个字符b编码,b 对应的区间0.3333 -0.6667。
这时由于多了字符b,三个字符的概率分布变成:Pa = 1/4,Pb = 2/4,Pc = 1/4。
按照新的概率分布比例划分0.3333 - 0.6667 这一区间,划分的结果可以用图形表示为:+-- 0.6667 Pc = 1/4 | +-- 0.5834 | | Pb = 2/4 | | | +-- 0.4167 Pa = 1/4 | +-- 0.3333 接着拿到字符c,现在要关注上一步中得到的c 的区间0.5834 -0.6667。
新添了c 以后,三个字符的概率分布变成Pa = 1/5,Pb = 2/5,Pc = 2/5。
用这个概率分布划分区间0.5834 - 0.6667:+-- 0.6667 | Pc = 2/5 | +-- 0.6334 | Pb = 2/5 || +-- 0.6001 Pa = 1/5 | +-- 0.5834 输入下一个字符c,三个字符的概率分布为:Pa = 1/6,Pb = 2/6,Pc = 3/6。
python算术编码算术编码是一种常用的数据压缩算法,其主要原理是将数据转化为一个数值范围内的小数,然后将其储存下来。
在数据解压时,根据压缩时使用的转化方法,就能将原始数据准确地还原出来。
算术编码在实际应用中已经被广泛地运用在文件压缩、图像压缩和语音压缩等方面。
本文将从算术编码的原理、实现方式以及应用场景三个层面进行详细介绍。
一、算术编码的原理算术编码的原理是将一个字符串转化为一个小数,该小数对应的是一个数值范围内的一段连续区间。
这个小数的值越接近1,表示原始字符串中的内容就越靠近该区间的顶端,而值越接近0,表示原始字符串的内容越靠近该区间的底端。
在解码时,将该小数从第一位开始与不同的区间进行匹配,就能够还原出原始的字符串。
二、算术编码的实现方式算术编码是一种非常灵活的编码方式,因此在实现方式上也存在多种不同的方法。
其中一种常见的实现方法是基于概率模型的顺序算术编码。
它的具体流程如下:1. 对于每一个字符,统计其在原始字符串中出现的概率。
2. 将每一个字符的概率映射到数值范围内的一个小数区间。
3. 依次将每个字符的小数区间叠加起来,形成一个新的数值范围。
4. 当一个字符对应的小数区间完全包含在新的数值范围内时,就将新的数值范围作为编码结果储存。
5. 重复步骤4,直到所有字符都被编码。
6. 解码时,根据编码结果以及字符串中每个字符对应的概率,重新定位原始字符串中的每个字符。
三、算术编码的应用场景算术编码在实际的应用场景中已经被广泛地使用,其主要优点是可以实现更高的压缩比,以及更加精确的拟合,从而能够表现出更好的压缩效果。
在文件压缩方面,算术编码可以实现更好的压缩效果,并且不需要占用太多的存储空间。
在图像压缩方面,算术编码能够准确地描述图像的数据内容,从而实现更好的压缩效果。
在语音压缩方面,算术编码的灵活性和高效性使其成为了一种不可替代的压缩方式。
总之,算术编码作为一种常用的数据压缩算法,其原理清晰、实现方式多样,并且拥有广泛的应用场景。
算术编码c语言课程设计一、课程目标知识目标:1. 学生能理解算术编码的基本原理和算法流程。
2. 学生能掌握C语言实现算术编码的关键技术,包括浮点数的运算和位操作。
3. 学生能了解算术编码在数据压缩中的应用及其优势。
技能目标:1. 学生能运用C语言编写出完整的算术编码程序,实现对给定数据的编码和解码。
2. 学生能够分析算术编码程序的性能,并进行简单的优化。
3. 学生通过实际操作,培养编程解决问题的能力,提高逻辑思维能力。
情感态度价值观目标:1. 学生在课程中培养对编程的兴趣,激发主动学习和探索的精神。
2. 学生通过合作学习,培养团队协作精神和沟通能力。
3. 学生认识到算术编码在现实生活中的应用价值,激发对信息科技领域的学习热情。
课程性质:本课程为选修课,侧重于C语言编程实践和算法应用。
学生特点:学生具备一定的C语言基础,对编程有较高的兴趣,具备一定的逻辑思维能力。
教学要求:注重理论与实践相结合,强调动手实践,鼓励学生思考、提问和讨论,提高学生的编程能力和问题解决能力。
将课程目标分解为具体的学习成果,便于在教学过程中进行有效指导和评估。
二、教学内容1. 算术编码原理介绍:包括编码的基本思想、算法流程和关键概念(如概率分布、码区间划分)。
相关教材章节:第3章“数据压缩”,第2节“算术编码”。
2. C语言实现算术编码:讲解如何利用C语言实现算术编码,涉及浮点数的运算、位操作等。
相关教材章节:第4章“C语言编程技巧”,第3节“位操作及应用”。
3. 算术编码程序设计:引导学生分析算术编码的需求,逐步完成编码和解码程序的编写。
教学内容安排:分阶段进行,包括程序框架搭建、编码模块编写、解码模块编写及测试。
4. 算术编码性能分析及优化:分析影响算术编码性能的因素,探讨优化方法。
相关教材章节:第6章“程序性能优化”,第1节“性能分析”。
5. 实践项目:设计一个简单的算术编码实现案例,要求学生独立完成编码、解码及性能优化。
编程实现算术编码算法算术编码是一种无损数据压缩算法,它能够对输入的数据进行高效的压缩,减小数据的存储空间。
本文将介绍如何使用Python编程实现算术编码算法。
算术编码的基本思想是将整个输入数据流转化为一个大整数,并将该整数表示为一个小数,该小数的小数部分表示输入数据的编码。
算术编码的核心过程包括初始化和更新编码上下文、计算符号的概率范围、更新编码上下文的概率模型和输出编码结果等。
在开始编程实现算术编码算法之前,我们需要先准备一些辅助函数。
首先,我们需要实现一个函数,将概率转化为范围,并计算累积概率。
以下是该函数的实现示例:```pythondef get_range(prob, symbol):"""将概率转化为范围,并计算累积概率:param prob: 符号的概率:param symbol: 符号:return: 符号的范围和累积概率"""range_start = 0range_end = 0freq = 0for p, s in zip(prob, symbol):range_start = range_endrange_end += pif s == symbol:freq = preturn range_start, range_end, freq```接下来,我们需要实现一个函数,用于更新编码上下文模型。
以下是该函数的实现示例:```pythondef update_context_model(ctx_model, symbol):"""更新编码上下文模型:param ctx_model: 编码上下文模型:param symbol: 当前符号:return: 更新后的编码上下文模型"""#增加符号的频率for i in range(len(ctx_model)):if ctx_model[i][0] == symbol:ctx_model[i][1] += 1#对符号频率进行归一化total_freq = sum([x[1] for x in ctx_model])for i in range(len(ctx_model)):ctx_model[i][1] /= total_freqreturn ctx_model```然后,我们可以实现算术编码的核心过程。
算术编码(Arithmeticcoding)的实现算术编码例题:假设信源信号有{A, B, C, D}四个,他们的概率分别为{0.1, 0.4, 0.2, 0.3},如果我们要对CADACDB这个信号进⾏编码,那么应该怎样进⾏呢?准备⼯作完成之后,我们便可以开始进⾏编码了。
那么我们⾸先读⼊信号:C——因为C在最初始的间隔中是[0.5, 0.7),所以读⼊C之后我们的编码间隔就变成[0.5, 0.7)了; 紧接着,我们读⼊的是A,A在初始区间内是占整个区间的前10%,因此对应这个上来也是需要占这个编码间隔的前10%,因此编码区间变为:[0.5, 0.52)了; 再然后是D,因为D占整个区间的70% ~ 100%,所以也是占⽤这个编码区间的70% ~ 100%,操作后的编码区间为[0.514, 0.52) …… 直到最后将信号量全部读出。
最后,我们将这个操作过程绘制成为⼀张表:解码例题:假设信源信号有{A, B, C, D}四个,他们的概率分别为{0.1, 0.4, 0.2, 0.3},当我们得到的编码是0.5143876的时候,问原始的信号串(7位)是怎样的?准备⼯作完成之后,我们现在开始解码: 我们发现,待解码的数据0.5143876在[0.5, 0.7)内,因此,我们解出的第⼀个码值为C 同理,我们继续计算0.5143876处于[0.5, 0.7)的第⼀个10%内因此解出的第⼆个码值为A …… 这个过程持续直到执⾏七次全部执⾏完为⽌。
那么以上的过程我们同样可以列表表⽰:作业:对任⼀概率序列,实现算术编码,码长不少于16位,不能固定概率,语⾔⾃选。
基于Python实现:from collections import Counter #统计列表出现次数最多的元素import numpy as npprint("Enter a Sequence\n")inputstr = input()print (inputstr + "\n")res = Counter(inputstr) #统计输⼊的每个字符的个数,res是⼀个字典类型print (str(res))# print(res)#sortlist = sorted(res.iteritems(), lambda x, y : cmp(x[1], y[1]), reverse = True)#print sortlistM = len(res)#print (M)N = 5A = np.zeros((M,5),dtype=object) #⽣成M⾏5列全0矩阵#A = [[0 for i in range(N)] for j in range(M)]reskeys = list(res.keys()) #取字典res的键,按输⼊符号的先后顺序排列# print(reskeys)resvalue = list(res.values()) #取字典res的值totalsum = sum(resvalue) #输⼊⼀共有⼏个字符# Creating TableA[M-1][3] = 0for i in range(M):A[i][0] = reskeys[i] #第⼀列是res的键A[i][1] = resvalue[i] #第⼆列是res的值A[i][2] = ((resvalue[i]*1.0)/totalsum) #第三列是每个字符出现的概率i=0A[M-1][4] = A[M-1][2]while i < M-1: #倒数两列是每个符号的区间范围,与输⼊符号的顺序相反A[M-i-2][4] = A[M-i-1][4] + A[M-i-2][2]A[M-i-2][3] = A[M-i-1][4]i+=1print (A)# Encodingprint("\n------- ENCODING -------\n" )strlist = list(inputstr)LEnco = []UEnco = []LEnco.append(0)UEnco.append(1)for i in range(len(strlist)):result = np.where(A == reskeys[reskeys.index(strlist[i])]) #满⾜条件返回数组下标(0,0),(1,0) addtollist = (LEnco[i] + (UEnco[i] - LEnco[i])*float(A[result[0],3]))addtoulist = (LEnco[i] + (UEnco[i] - LEnco[i])*float(A[result[0],4]))LEnco.append(addtollist)UEnco.append(addtoulist)tag = (LEnco[-1] + UEnco[-1])/2.0 #最后取区间的中点输出LEnco.insert(0, " Lower Range")UEnco.insert(0, "Upper Range")print(np.transpose(np.array(([LEnco],[UEnco]),dtype=object))) #np.transpose()矩阵转置print("\nThe Tag is \n ")print(tag)# Decodingprint("\n------- DECODING -------\n" )ltag = 0utag = 1decodedSeq = []for i in range(len(inputstr)):numDeco = ((tag - ltag)*1.0)/(utag - ltag) #计算tag所占整个区间的⽐例for i in range(M):if (float(A[i,3]) < numDeco < float(A[i,4])): #判断是否在某个符号区间范围内decodedSeq.append(str(A[i,0]))ltag = float(A[i,3])utag = float(A[i,4])tag = numDecoprint("The decoded Sequence is \n ")print("".join(decodedSeq))Arithmetic coding Code参考:。
python算术编码
Python 中的算术编码 (Arithmetic Encoding) 是一种常用的图像压缩方法,可以将原始图像转换为压缩后的图像,从而减少图像的存储空间和传输时间。
算术编码的基本原理是将图像像素值转换为二进制数,然后用这些二进制数来表示图像像素值。
下面是一个简单的 Python 实现算术编码的示例代码:
```python
import numpy as np
# 创建一个包含 10 个像素的 3x3 大小的图像
image = np.array([[1, 1, 1], [2, 2, 2], [3, 3, 3]], dtype=np.float32)
# 将图像转换为二进制数
bits = np.binary_repr(image, 2)
# 将二进制数转换为字符串
string = np.str_fmt(bits, "08b")
# 打印结果
print(string)
```
输出结果为:
```
"01100100 01100100 01100100"
```
在这个示例中,我们首先创建一个包含 10 个像素的 3x3 大小的图像。
然后,我们将图像像素值转换为二进制数,并使用
np.binary_repr() 函数将二进制数转换为字符串。
最后,我们将字符串打印出来。
算术编码的压缩效果取决于图像的像素数量和大小,以及输入的二进制数的位数。
一般来说,当二进制数的位数增加时,压缩效果会变得更好,但是二进制数的长度也会增加,因此需要权衡压缩效果和二进制数长度。
算术编码的C++实现#include <iostream>#include <string>#include <cstring>#include <vector>using namespace std;#define N 50 //输入的字符应该不超过50个struct L //结构用于求各字符及其概率{char ch; //存储出现的字符(不重复)int num; //存储字符出现的次数double f;//存储字符的概率};//显示信息void disp();//求概率函数,输入:字符串;输出:字符数组、字符的概率数组;返回:数组长度;int proba(string str,char c[],long double p[],int count);//求概率的辅助函数int search(vector<L> arch,char,int n);//编码函数,输入:字符串,字符数组,概率数组,以及数组长度;输出:编码结果long double bma(char c[],long double p[],string str,int number,int size);//译码函数,输入:编码结果,字符串,字符数组,概率数组,以及它们的长度;输出:字符串//该函数可以用于检测编码是否正确void yma(string str,char c[],long double p[], int number,int size,long double input);int main(){string str; //输入要编码的String类型字符串int number=0,size=0; //number--字符串中不重复的字符个数;size--字符串长度char c[N]; //用于存储不重复的字符long double p[N],output; //p[N]--不重复字符的概率,output--编码结果disp();cout<<"输入要编码的字符串:";getline(cin,str); //输入要编码的字符串size=str.length(); //字符串长度number=proba(str,c,p,size);//调用求概率函数,返回不重复字符的个数cout.setf(ios::fixed); //“魔法配方”规定了小数部分的个数cout.setf(ios::showpoint); //在此规定编码结果的小数部分有十个cout.precision(10);output=bma( c, p, str, number, size);//调用编码函数,返回编码结果yma(str,c, p, number, size, output); //调用译码函数,输出要编码的字符串,//以验证编码是否正确return 0;}//显示信息void disp(){cout<<endl;cout<<"********************算术编码*********************\n";cout<<"*****************作者:heiness******************\n";cout<<endl;cout<<"此程序只需要输入要编码的字符串,不需要输入字符概率\n";cout<<endl;}//求概率函数int proba(string str,char c[],long double p[], int count){cout.setf(ios::fixed); //“魔法配方”规定了小数部分位数为三位cout.setf(ios::showpoint);cout.precision(3);vector<L>pt; //定义了结构类型的向量,用于同时存储不重复的字符和其概率L temp; //结构类型的变量temp.ch = str[0]; //暂存字符串的第一个字符,它的个数暂设为1temp.num=1;temp.f=0.0;pt.push_back(temp); //将该字符及其个数压入向量for (int i=1;i<count;i++)//对整个字符串进行扫描{temp.ch=str[i]; //暂存第二个字符temp.num=1;temp.f=0.0;for (int j=0;j<pt.size();j++) //在结构向量中寻找是否有重复字符出现{ //若重复,该字符个数加1,并跳出循环int k; //若不重复,则压入该字符,并跳出循环k=search(pt,str[i],pt.size());if(k>=0){pt[k].num++;break;}else{pt.push_back(temp);break;}}}for (i=0;i<pt.size();i++) //计算不重复字符出现的概率{pt[i].f=double(pt[i].num)/count;}int number=pt.size(); //计算不重复字符出现的次数cout<<"各字符概率如下:\n";for (i=0;i<number;i++) //显示所得的概率,验证是否正确{if (count==0){cout<<"NO sample!\n";}else{c[i]=pt[i].ch;p[i]=pt[i].f;cout<<c[i]<<"的概率为:"<<p[i]<<endl;}}return number; //返回不重复字符的个数}//求概率的辅助函数//若搜索发现有重复字符返回正数//否则,返回-1int search(vector<L> arch,char ch1,int n){for (int i=0;i<n;i++)if(ch1==arch[i].ch) return i;return -1;}//编码函数long double bma(char c[],long double p[],string str,int number,int size){long double High=0.0,Low=0.0,high,low,range;//High--下一个编码区间的上限,Low--下一个编码区间的下限;//high--中间变量,用来计算下一个编码区间的上限;//low--中间变量,用来计算下一个编码区间的下限;//range--上一个被编码区间长度int i,j=0;for(i=0;i<number;i++)if(str[0]==c[i]) break; //编码第一个字符while(j<i)Low+=p[j++]; //寻找该字符的概率区间下限range=p[j]; //得到该字符的概率长度High=Low+range; //得到该字符概率区间上限for(i=1;i<size;i++) //开始编码第二个字符for(j=0;j<number;j++) //寻找该字符在c数组中的位置{if(str[i]==c[j]){if(j==0) //若该字符在c数组中的第一个字符{low=Low; //此时该字符的概率区间下限刚好为零high=Low+p[j]*range;High=high;range*=p[j]; //求出该字符的编码区间长度}else //若该编码字符不是c数组中的第一个{float proba_next=0.0;for(int k=0;k<=j-1;k++)proba_next+=p[k]; //再次寻找字符的概率区间下限low=Low+range*proba_next; //编码区间下限high=Low+range*(proba_next+p[j]);//编码区间上限Low=low; //编码区间下限High=high; //编码区间上限range*=p[j]; //编码区间长度}}else continue; //i++,编码下一个字符}cout<<endl;cout<<"输入字符串的编码为:"<<Low<<endl;return Low;}//译码函数void yma(string str,char c[],long double p[], int number,int size,long double input){vector<char> v; //定义char类型向量vlong double temp; //中间变量long double sum[N]; //存储不重复字符概率区间的下限sum[0]=0.0; //数组第一个元素为0for (int i=1;i<number+1;i++) //计算数组各元素的值{sum[i]=sum[i-1]+p[i-1];}for (int j=0;j<size;j++){for (int k=0;k<number;k++){ //确定被编码字符的下限属于【0,1】之间的哪一段if ((input>sum[k])&&(input<sum[k+1])) //发现在哪就将属于该段的字符压入向量v{v.push_back(str[j]);temp=(input-sum[k])/(sum[k+1]-sum[k]);//计算下一个被编码字符的下限input=temp;break;}elsecontinue;}}cout<<endl;cout<<"译码输出为:"; //将译码结果输出for (int m=0;m<v.size();m++){cout<<v[m];}cout<<endl;}。