用分组编码解决算术编码的精度要求问题

article/2025/9/15 4:15:52

这篇博客要介绍的是算术编码、译码。主要用分组编码的思路解决了当消息比较长时,小数位数太多,计算工具精度达不到的问题。 文末给出了matlab代码。题目的要求是:已知26个英文字母和空格的统计概率,对文本文档中的消息(很长的消息,比如一篇英文小作文)进行算术编码译码。概率分布如下:
信源符号概率分布
首先简单介绍下算术和编码:算术编码是数据压缩的主要算法之一。 是一种无损数据压缩方法,也是一种熵编码的方法。和其它熵编码方法不同的地方在于,其他的熵编码方法通常是把输入的消息分割为符号,然后对每个符号进行编码,而算术编码是直接把整个输入的消息编码为一个数,一个满足(0.0 ≤ n < 1.0)的小数n。

编码译码流程:

1)首先,按照各信源符号的概率分布,将[0, 1)这个区间分成若干个子区间,那么每个符号就会有自己对应的区间;
2)将[0, 1)这个区间设置为初始间隔,读入一个符号读入,判断该符号落入哪一区间。然后将该区间按照[0, 1)上区间划分等比例的划分出新的子区间,等待下一个符号的落入;
3)然后依次迭代,不断重复进行步骤2,直到最后信源符号全部读完为止。

译码的前提是已知信源符号的概率分布。译码的过程为:
1)根据信源符号的概率分布,将[0, 1)这个区间分成若干个子区间,每个子区间对应一个信源符号;
2)读入编码结果,判断编码结果落在哪一子区间,对应符号即为该为译码结果。然后将该区间按照[0, 1)上区间划分等比例的划分出新的子区间,等待下一次比较;
3)然后依次迭代,不断重复进行步骤2,直到译出全部的符号。

下面以一个简单的例子来说明算术编码译码的流程。
算术编码流程
设信源符号以及概率分布为{A:0.4,B:0.4,C:0.2},要编码的消息为‘ACB’根据概率分布对[0,1]划分,可以画出概率轴,可以看到概率轴上,ABC分别对应各自区间。每一次编码都要确定一次概率区间。首先第一个符号A确定了区间为[0,0.4],然后对[0,0.4]按照符号的概率分布再次划分,画出第二个概率轴,第二个符号C对应的区间为[0.32,0.4],再对[0.32,0.4]按照符号的概率分布再次划分,画出第三个概率轴,第三个符号B对应的区间为[0.352,0.384],到这里,编码结束,如果是更长的消息,那么以此类推。最终区间为[0.352,0.384],我们取中点0.368,将其转化为二进制数,就是算术编码的结果,可以对这个二进制数进行接下来的信道编码、调制等等操作。

译码时同样先把二进制转换为小数,生成概率轴,不断的判断小数位于哪一个区间,以确定对应哪一个字母,直到译出全部字母。
**

算术编码的性能分析

**
算术编码与霍夫曼编码都是熵编码,即概率越小的字符,用更多的bit去表示,这反映到概率区间上就是,概率小的字符所对应的区间也小,因此这个区间的上下边际值的差值越小,为了唯一确定当前这个区间,则需要更多的数字去表示它。对于霍夫曼编码,每个信源符号都有准确且唯一的码字与之对应,但是由于位数只能取整数,所以每个信源符号的位数与它的概率并不是严格的线性关系,而是有一定的近似。对于算术编码,每个符号并没有准确唯一的码字与之对应,根据编码流程知道一条完整的信源消息会对应一个编码结果,如果按照霍夫曼编码的思想考虑码字与符号的对应关系,会发现不同码字的符号之间的界限并不是明确的,而是有重叠的,这从一个角度解释了算术编码可以比霍夫曼编码更加逼近香农极限。

但是算术编码存在的一个问题就是,当一条消息很长时,理论上编码结果的小数的精度就越高,当超过了运行环境的计算精度时,会发生精度溢出,造成编码失败。在仿真过程中,我们就碰到了这样的问题,提供一个简单的解决思路,那就是分组编码。在编码前确定对每组多少个字母进行编码,不会出现精度问题,比如我在代码中将每组字母数设置为5,将一个很长的信息,等分成若干组,对每一组分别进行算术编码,译码时也是对每一组分别进行译码。那么在代码实现中,在对每一组编码完成以后,在二进制序列后面加了一个空格作为标识符,用来隔开不同组的编码结果。在译码时,不断的读取编码序列,每读到一个空格,就对读到的序列进行译码,这是一组的译码结果。不断的重复这个过程,直到序列都被译码成消息。

**

代码实现

**
这道题关键在于要传递的消息很长,一口气编完显然是不可能的,小数点后面不知多少位了,那么就需要用分组编码的思想。做作业的过程中,没有找到很合适的参考代码,于是自己试着写了下,有些地方可能不是很合适,欢迎大家和我交流~
主程序很简单,只有简单的几行,然后主要的几个函数,编码函数、译码函数、码长计算函数、十进制小数转二进制函数和二进制转十进制小数函数,可能需要仔细看一看。要想成功运行程序,首先要自己建立一个send.txt,里面存要编码的消息,注意,不要有标点。

clc
sym=['a' 'b' 'c' 'd' 'e' 'f' 'g' 'h' 'i' 'j' 'k' 'l' 'm' 'n' 'o' 'p' 'q' 'r' 's' 't' 'u' 'v' 'w' 'x' 'y' 'z' ' ' ];
p=[0.0575 0.0128 0.0263 0.0285 0.0913 0.0173 0.0133 0.0313 0.0599 0.0006 0.0084 0.0335 0.0235 0.0596 0.0689 0.0192 0.0008 0.0508 0.0567 0.0706 0.0334 0.0069 0.0119 0.0073 0.0164 0.0007 0.1928];
allmessage=fileread('send.txt');%从send.txt中读取消息 
allmessage=lower(allmessage);%把大写字母变成小写  
allmessage=regexprep(allmessage,{','},' ');%去掉逗号,如果其它标点,请去掉 
code1= arithCode(allmessage,sym,p,5);%编码,每次编5个字母 
decode1=arithDecode(code1,sym,p,5);%译码 
fid=fopen('result.txt','w');%把译码结果存入result.txt中 
fprintf(fid,'%s',decode1);
fclose(fid);%以下是用到的函数和具体的说明  
%编码函数
function [ codeBin ] = arithCode( message,alphaDic,alphaProb,symNum)
%对输入的消息序列进行算术编码 
%  codeBin 输出的二进制编码序列 
%  message  输入的消息序列  
%  alphaDic  信源符号集合 
%  alphaProb  信源符号对应的概率
%  symNum    进行一次算术编码对多少个符号进行编码
probValOri(1)=0;%字母a对应的区间起点是0 
for i=1:length(alphaDic)probValOri(i+1)=probValOri(i)+alphaProb(i);%把字母按照概率分布对应给[0,1]上的不同概率区间,生成概率轴
end
totalLen=length(message);%要编码的消息的长度 
operaNum=floor(totalLen/symNum);%商是整数,为编码次数;非整数,则为编码次数-1 
restSymNum=mod(totalLen,symNum);%最后一次算术编码要处理的字母个数 
codeBin=[];%编码后的二进制序列  
%按照次数遍历 
for k=0:operaNum-1left=0;%区间左界 valLen=1;%区间长度  probVal=probValOri;shortMes=message(k*symNum+1:(k+1)*symNum);%每次处理symNum个字母 %每一次对symNum个字母的编码流程如下 for i=1:symNumleft=left+probVal(find(alphaDic==shortMes(i)));%确定第i个字母在概率轴的位置,左界 right=left+alphaProb(find(alphaDic==shortMes(i)))*valLen;%左界加这个字母对应的概率为右界 %每进行一次编码,原始概率分布都要乘以区间长度进行缩小valLen=right-left;probVal=probValOri*valLen;%概率轴也要按照区间长度缩小,为下一个字母编码准备endmiddle=0.5*(right+left)%编好的小数 codeLen=calcCodeLen(alphaProb,alphaDic,shortMes);%计算编码长度 shortCodeBin=deciConvertBin(middle,2*codeLen);%将小数转换成指定长度的二进制序列 shortCodeBin=[shortCodeBin ' '];%每一次编码完成后,末尾加空格,隔开下一次的序列  codeBin=[codeBin shortCodeBin];%将编码序列存入 shortCodeBin=[];
end
%如果商不为0,对最后剩下的字母进行编码,和上面的流程类似
if(restSymNum~=0)left=0;valLen=1;probVal=probValOri;for j=totalLen-restSymNum+1:totalLenleft=left+probVal(find(alphaDic==message(j)));right=left+alphaProb(find(alphaDic==message(j)))*valLen;valLen=right-left;probVal=probValOri.*valLen;endmiddle=0.5*(right+left); codeLen=calcCodeLen(alphaProb,alphaDic,message(totalLen-restSymNum+1:totalLen));shortCodeBin=deciConvertBin(middle,2*codeLen);shortCodeBin=[shortCodeBin ' '];codeBin=[codeBin shortCodeBin];
end
end%译码函数
function [ mesDecode] = arithDecode(codeBin,alphaDic,alphaProb,symNum )
%  对编好的二进制序列进行算术译码 
%  codeBin是编好的二进制序列  
%  alphaDic  信源符号集合 
%  alphaProb  信源符号对应的概率 
%  mesDecode  输出的译码消息 
%  symNum    进行一次算术编码对多少个符号进行编码
shortMes=[];mesDecode=[];shortCodeBin=[];%一次译码所得消息、全部消息、一次译码要处理的二进制序列
%每检测到一次空格,就停止向shortCodeBin中读入数字,对已经读入的二进制序列译码
for i=1:length(codeBin)if(codeBin(i)~=' ')shortCodeBin=[shortCodeBin codeBin(i)];elsecodeDec=deciConvertDec(shortCodeBin);%把二进制序列转化为十进制小数 probValOri(1)=0;%按照字母的概率分布,生成概率轴 for j=1:length(alphaDic)probValOri(j+1)=probValOri(j)+alphaProb(j);endleft=0;val=1;probVal=probValOri;%已知一次编译码处理symNum个字母  for k=1:symNum%通过判断codeDec位于概率轴的哪个区间,判断字母是什么 for m=1:length(alphaDic)if(codeDec>=left+probVal(m)&&codeDec<=left+probVal(m+1))shortMes=[shortMes alphaDic(m)];break;endend%每译码一个字母,更新区间长度、左界和概率轴val=probVal(m+1)-probVal(m);left=left+probVal(m);probVal=probValOri.*val;    endmesDecode=[mesDecode shortMes];shortMes=[];shortCodeBin=[];end
end
end%计算消息码长
function [ codeLen ] = calcCodeLen(prob,alpha,message)
%计算码长,根据信息论,计算一个字符串的信息量,单位为bit 
multiProb=1;
for i=1:length(message)multiProb=multiProb*prob(find(alpha==message(i)));
end
codeLen=ceil(-log2(multiProb));
end%十进制小数转二进制
function [ bin ] = deciConvertBin( deci,codeLen )
%将十进制小数转化为规定长度的二进制序列  
%这段代码有一个bug,就是没有考虑当bin全是1时的进位  
bins=[];
for i=1:codeLendeci=2*deci;inte=floor(deci);deci=deci-inte;inteStr=num2str(inte);bins=[bins inteStr];
end
for j=codeLen:-1:1if(bins(j)=='0')bins(j)='1';break;elsebins(j)='0';end
end 
bin=bins;
end%二进制序列转十进制小数
function [ dec ] = deciConvertDec(bin )
%二进制小数转化成十进制小数(0、1之间)
%Convert binary decimals to decimal decimals (between 0 and 1)
bins=[];
for j=1:length(bin)bins(j)=str2num(bin(j));
end
dec=0;
for i=1:length(bins)dec=dec+2.^(-i)*bins(i);
end
end

http://chatgpt.dhexx.cn/article/xTqK3Ca1.shtml

相关文章

算术编码 matlab程序,算术编码算法的matlab实现

算术编码算法的matlab实现 实验 1 算术编码算法的 Matlab 实现实验学时&#xff1a;2实验类型&#xff1a;(演示、验证、综合、√设计、研究)实验要求&#xff1a;(√必修、选修)一、实验目的掌握算数编码原理。二、实验内容利用 Matlab 编写程序实现算数编码&#xff0c;包括…

十六、算术编码_2、算术编码举例实现

基本原理 在一次算术编码的执行前,为简便起见,首先假设输入的信源为0/1的二进制信源,0和1的概率比为7:3。即二者的概率为: p(0) = 0.7; p(1) = 0.3;假设输入的待编码信息为[0, 0, 1],在编码每一个符号时,都需要对概率区间进行分割,并通过与编码区间进行比较,判断是否…

信息论基础:算术编码

1 引言 霍夫曼码是一种无损编码&#xff0c;而且是最优的符号码。但是&#xff0c;它有两个缺点&#xff1a;&#xff08;1&#xff09;每个符号至少需要一个比特&#xff1b;&#xff08;2&#xff09;当符号的概率分布变化时&#xff0c;使用不方便。 用一个例子来看看霍夫…

多媒体数据处理实验1:算术编码

1. 算法描述 功能&#xff1a; 给定概率字典以及待编码字符串&#xff0c;求出该字符串算术编码的结果&#xff08;最短二进制串&#xff09;&#xff0c;并能根据算数编码结果进行解码&#xff0c;得到原字符串。 2.算法流程&#xff1a; 算术编码流程&#xff1a; (1) 首先…

数字图像算术编码python_算术编码简介

上一篇讲了LZW编码&#xff0c;本篇讨论另一种不同的编码算法&#xff0c;算数编码。和哈夫曼编码一样&#xff0c;算数编码是熵编码的一种&#xff0c;是基于数据中字符出现的概率&#xff0c;给不同字符以不同的编码。本文也会对这两种编码方式的相似和不同点进行比较。 编码…

算术编码

文章首发于我的个人博客 前言 这篇博客主要总结大二下课程《信息论》实验的内容。主要包含固定模式的算数编码以及自适应模式的算术编码。我将首先介绍这两种算术编码的基本思想和实现思路&#xff0c;然后给出具体的python代码并对代码中的一些关键点进行解释说明。 固定模…

算术编码原理及其python实现

目录 1. 原理部分&#xff1a;2. 香农界理论分析&#xff1a;3. 代码实现&#xff1a;4.实验结果 1. 原理部分&#xff1a; 原理部分参考什么是算术编码 一个从信源序列到不可压缩二元序列的一个可逆映射&#xff0c;假设序列 { X 1 … X n } \{X_{1} \ldots X_{n}\} {X1​…X…

基本算术编码

1.基本思想 算术编码&#xff0c;就是用一个数编码一串字符串。它的思想是这样的:对一个需要编码的字符串&#xff0c;给出一个初始区间[0, 1),这个区间被分成n份&#xff0c;n是这串字符串中不同字符的个数&#xff0c;每一份占区间长度的比例与相应字符出现次数占整个字符串…

算术编码的基本概念

二、算术编码的基本概念 算术编码属于熵编码的一种重要的类型&#xff0c;其作用同变长编码等熵编码方法类似&#xff0c;用于压缩输入数据中的统计冗余&#xff0c;并且使用算术编码的压缩同样是无损压缩。 在本系列第1篇中讨论了典型的变长编码方法——哈夫曼编码。包括哈夫…

Otsu大津算法公式推导及python实现

目录 前言 一、类间平方差是什么&#xff1f; 二、公式推导及实现 1.求类间平方差 2.opencv-python编程实现 2.1 引入图像并灰度化 2.2 查看灰度值的分布情况 2.3 求全局平均阈值 2.4 求最大类间方差 3.算法的验证 总结 前言 OTSU&#xff08;大津算法&#xff09;是…

OTSU算法原理

OTSU算法原理及实现&#xff1a; 最大类间方差是由日本学者大津(Nobuyuki Otsu)于1979年提出&#xff0c;是一种自适应的阈值确定方法。算法假设图像像素能够根据阈值&#xff0c;被分成背景[background]和目标[objects]两部分。然后&#xff0c;计算该最佳阈值来区分这两类像素…

大津阈值分割算法(OTSU处理图像)

1.算法原理简述 对于图像I(x,y)&#xff0c;前景(即目标)和背景的分割阈值记作T&#xff0c;属于前景的像素点数占整幅图像的比例记为ω0&#xff0c;其平均灰度μ0&#xff1b;背景像素点数占整幅图像的比例为ω1&#xff0c;其平均灰度为μ1。图像的总平均灰度记为μ&#xf…

[图像处理]14.分割算法比较 OTSU算法+自适应阈值算法+分水岭

参考文献&#xff1a; OTSU阈值分割孔洞填充海陆分离_SwordKii的博客-CSDN博客 drawContours函数_普通网友的博客-CSDN博客_drawcontours R329-opencv阈值分割算法——自适应阈值_Third Impact的博客-CSDN博客_opencv自适应阈值分割 分水岭算法的python实现及解析_进不去的…

OTSU算法 (大津算法)理解代码

OTSU算法&#xff1a;对图像进行二值化的算法 介绍 OTSU算法是一种自适应的阈值确定的方法&#xff0c;又称大津阈值分割法&#xff0c;是最小二乘法意义下的最优分割。 它是按图像的灰度特性&#xff0c;将图像分成背景和前景两部分。因方差是灰度分布均匀性的一种度量,背景…

Pr-快速上手-基本操作-教程

视频链接: Pr教程 视频设计到的知识点: 视频的剪辑bgm的管理添加字幕及弹幕的添加鬼畜视频的制作发布教程

Pr常用操作技巧

操作技巧持续更新中 1.premiere如何裁剪视频尺寸 视频尺寸怎么修改 1.将premiere左下角的视频素材直接向右拖动到编辑区中 2.鼠标左键单击视频,然后选择上方的【效果】 3.这时,页面左侧将弹出效果的菜单栏,依次选择效果→视频效果→变换→裁剪 4.鼠标左键按住【裁剪】不放,直接…

Premiere: 基本操作

1、首先进入编辑模式&#xff0c;使得视频能加效果 2、这时候能找到视频的效果添加&#xff0c;这里有视频的水平翻转&#xff0c;高斯模糊等&#xff0c;需要什么效果直接&#xff0c;把该效果抓到视频上就可以。

PR的入门基础教程

提示&#xff1a;这里只记述作者学习PR入门基础教程视频后的总结 文章目录 常用视频概念第1天学习总结第2天学习总结第3天学习总结 常用视频概念 第1天学习总结 第2天学习总结 第3天学习总结

Adobe Premiere Pro快速入门教程

简介&#xff1a; 适用于纯新手零基础&#xff0c;看完本教程即可完成常用视频编辑技巧。 采用Adobe Premiere Pro 2020版本 windows10操作系统 一、制作 照片音频字幕的视频 目标&#xff1a;把三张图片和一个音乐做成带字幕的视频。&#xff08;素材请自行准备&#xff0…

使用pr的8大技巧

许多小伙伴是通过pr这个软件进行素材剪辑的&#xff0c;当我们面对许多素材需要剪辑的时候&#xff0c;往往被这些素材弄得头昏脑涨&#xff0c;剪辑拼接的费时费力&#xff0c;最后出来的成品效果也不太好&#xff0c;下面就告诉大家一些pr使用的技巧&#xff0c;来提升我们的…