一、概要

在项目开发中,有引入用到rANS熵编码压缩算法,在使用的背后,想看看其运行的基本原理,也算补一下个人的熵编码知识。这里提到的熵编码压缩算法都是无损压缩。很久没有写文章了,太忙了,不知道一年一篇文章算不算年更 :b

二、熵编码

目前较为成熟的熵编码是霍夫曼编码,算术编码,以及14年Duda提出的ANS(Asymmetric Numeral Systems 非对称数系)编码。先解释一下霍夫曼编码和算术编码,然后重点说一下ANS编码的原理。

2.1 香农熵编码

熵在编码中,是对信息的衡量,熵越大,表明所包含的信息越多。对于高频出现的事件,其本身包含的信息其实是不多的,所以其对应的熵更小。而低频出现的事件,其包含的信息更多,对应的熵更大。香农的熵编码理论值计算公式为:

 ...(1)

2.2 霍夫曼编码

霍夫曼编码是速度最快的熵编码,其基本原理是基于统计的频率,构建二叉树,最后高频率的字符用最短的编码表示,最低频率的字符用最长的编码来表示。其基本的操作就是不断构建二叉树的过程,借鉴示例用图1:

基本操作就是取频率最低的2个字符,搭建一颗二叉树,然后根节点频率为叶子节点之和,如此递归,得到最终的二叉树,示例中的编码结果:

a: 0

b: 10

c: 110

d: 111

用编码替换输入的字符,即可得到最终的编码结果。 霍夫曼编码总结就是2个操作:构建霍夫曼树,执行霍夫曼编码。霍夫曼是执行速度最快的熵编码,但是其不能无限接近熵编码的理论值。

2.3 算术编码

算术编码是一种无限接近熵编码理论值的编码,其本质操作就是用一个[0, 1)的小数来表示最终的编码结果,其基本操作也是基于统计来进行的,用示例图来表示最直观[2]:

当前编码的字符为ABC三种字符,如何编码“BCCB”这个字符? 1)设定初始频率值,三种字符均值分布,则均为1/3,划分初步的概率分布; 2)输入B,其位于[0.333,0.667),则以此区间进行下一次划分,这是各个字符出现的频率进行更新,分别为1/4, 2/4, 1/4,得到最新的区间划分; 3)依次递推,最后编码所在区间为[0.639,0.6501),输出这个区间内的一个小数,例如0.64,转换为对应的二进制数即为最终的编码结果。 算术编码是一种能够接近理论值的熵编码,对应的代价就是算术的过程,速度慢。

三、ANS熵编码

项目中用到的编码是最近几年提出的一种新的熵编码,本着查看原理的心理去探究了一下这种最新的编码,很多文章都说的较为晦涩,不是我这样的小白能够理解的。在偶然拜读到一位国外大佬的文章后,通过详细的推导,总算大致了解了基本的实现原理,这里推荐有时间的可以看这篇英文原文:

Lossless Compression with Asymmetric Numeral Systems 结合这篇文章,我大致讲讲个人的理解:

3.1 将二进制字符串编码成自然数

从最简单的编码开始,假设一个字符串是以0/1字符串组成,如果用进制转换,我们都知道如何将其转换为10进制数。让我们展开来看:

假设我们已经转换了二进制字符串

, 其对应的数值为

,如果我们得到一个新的输入字符

,我们希望基于一个基本的编码函数来得到输出的数值,假设为:

 ....(2)

基于离散数学教程,如果还记得的话,这个公式是这样:

 ...(3) 为了区别"0"、“00”等情况,我们设定初始值:

, 反过来,我们可以从一个10进制数转换成对应的二进制字符串,其对应的函数可以表示为:

...(4)

举例来说明:

基于公式3和4,将字符

转换成一个十进制数,

, 那么其转换操作为:

其对应的解码过程为:

最终“10011”二进制字符串,转换为十进制数为51,需要用

个bit来表示,相对于理论极限值,多了一位,注意这里的解压结果相对输入字符串是倒序的,一般应用的时候,会先将输入倒序排列,这样解压得到结果就是正序的结果。

3.2 编码函数推导

上面的编码都是基于0和1字符串是均值分布的前置条件的,实际情况中,是很大可能出现不均值分布的情况的。

引用前面的公式2,

, 假设

具有

位的信息,如果我们想把

用理论值编码

位信息,那么可以推导公式:

 所以:

所以,我们可以得到这样的理论编码函数:

 ...(5)

3.3 Uniform Binary Variant(uABS)

现在我们将范围拓展,假设我们要编码的数字范围为[1,N], 用"1" 和"0"来分别表示奇数和偶数,对应的概率为

 和

 , 对应的在N个数字中,偶数出现的次数为

, 那么我们可以推导N+1 和 N之间的关系为:

 ...(6)

这里就不再详细的推证了,公式6等价于:

...(7) 基于公式7,我们就可以依次编码非均值分布的二进制数字符串了,其对应的解码公式为:

 =>

用实例来演示编码和解码的过程:

继续上面的用例

, 设定

,其编码过程为:

对应的解码过程为:

3.4 Range Variant(rANS)

上面的uABS是针对二进制字符的熵编码,我们也可以进一步的推广到非二进制字符的非均值熵编码。首先我们需要明确的是,公式5是依然生效的,只是在推广的时候,我们将

推广为

,也就是输入的二进制字符变成符号

即可。这样在新增一个字符的时候,对应的等价新增该字符的熵编码信息,所以公式5是依然生效的。

此外理论上来说,对于字符集,我们是可以有任意的概率分布的(只要字符集任意长),但是实际的时候我们是将其限定在一个量化范围内的,一般是

的范围。在这个范围内,符号

出现的次数为

,那么可以得到

, 基于这个量化,结合uABS的公式,可以用下面的公式来表示rANS的编码:

...(8)

对应的解码操作公式为:

....(9)

...(10)

其中

,可以理解为累计分布统计操作。

用示例来解释一下压缩和解压:

对于字符集['a', 'b', 'c'],其量化的 n = 3, 其统计的分布为

, 其对应的

, 现在我们来编码字符串"abc",对于初始值

, 我们设定为

,基于公式8可以得到编码过程为:

对应的解码操作为:

3.5 量化处理rANS

上面的rANS编码展示的是短字符的时候的编码流程,如果一个文件大小有1MB或者更大,这么长的字符串如何编码?如果直接编码的话,肯定会超过整数的表示范围,解决办法就是移位分解:

当编码

的时候,得到的结果过大时,将其右移M位来确保得到的结果处于

(例如M= 16bits)这个区间,同理在解压的时候,如果

较小,会将其左移M位,然后在进行处理,这样就能确保编码结果能用整形数来表示,其大致操作流程为:

MASK = 2**M -1

BOUND = 2**(2*M) - 1

##Encoding

s = readsymbol()

x_test = (x / f[s]) << n + (x % f[s]) + c[s]

if(x_test > BOUND):

write16bits(x & MASK)

x = x >> M

x = (x /f[s]) << n + (x % f[s]) + c[s]

##Decoding

s = symbol[x & MASK]

writeSymbol(s)

x = f[s](x >> n) + (x & MASK) -c[s]

if(x < 2**M):

x = x << M + read16bits()

对于ANS, 还有其他的编码,例如tANS编码,这里就不再讨论,还没看到这部分的编码。在实际的编码过程中,就是脱胎于上面的编码理论,进一步的完善编码上下文内容即可。

引用:

c语言香农编码文件压缩,谈谈熵编码无损压缩的原理相关推荐

  1. 计算信源熵和香农编码C语言,信息论与编码课程设计报告-统计信源熵与香农编码.pdf...

    信息论与编码课程设计报告 设计题目: 统计信源熵与香农编码 专业班级 电 信 12-06 学 号 学生姓名 指导教师 教师评分 2015 年 3 月 30 日 目 录 - 0 - 一.设计任务与要求 ...

  2. 香农编码二叉树c语言,shannon码的编码实验总结.docx

    shannon码的编码实验总结 本科生实验报告 实验课程信息理论与编码 学院名称信息科学与技术学院 专业名称 学生姓名 学生学号 指导教师 实验地点 实验成绩 二〇一六年九月----二〇一六年十一月 ...

  3. 香农费诺编码 c语言实现,对于香农编码、费诺编码和哈夫曼编码,编码方法惟一的是()。...

    问题标题 对于香农编码.费诺编码和哈夫曼编码,编码方法惟一的是(). 2019-8-15来自ip:15.170.14.227的网友咨询 浏览量:533 手机版 问题补充: 题目类型:[填空题] 对于香 ...

  4. C语言实现香农编码(二进制编码)

    用到的数据结构是双向链表: 环境:dev-c++ 5.11 对某一离散无记忆信源实现香农编码,输出消息符号及其对应的码字. 设离散无记忆信源 : 二进制香农编码过程如下: 1.将信源发出的N个消息符号 ...

  5. 信源编码的代码实现 (香农编码、费诺编码、哈夫曼编码、游程编码、算术编码)

    文章目录 香农编码 费诺编码 哈夫曼编码 C++版 C语言版 游程编码 算术编码 香农编码 (1) 将信源消息符号按其出现的概率大小依次排列 p1 ≥ p2 ≥ - ≥ pn (2) 确定满足下列不等 ...

  6. 2022P02014139杨智关于香农编码、哈夫曼编码和费诺编码的比较

    注:以下三种编码比较都是在二进制下讨论的,其它进制下的编码情况可以与二进制下的情况类比. 1香农编码 概念: 香农编码是是采用信源符号的累计概率分布函数来分配字码的.香农编码是根据香农第一定理直接得出 ...

  7. 天津理工大学 信息论与编码实验3 离散信源编码-香农编码

    一.实验目的 离散无记忆信源是一种最简单且最重要的信源,可以用完备的离散型概率空间来描述.本实验通过对给定的信源的进行相应的编码,加深对离散无记忆信源的无失真编码的理解. 掌握香农编码原理: 二.实验 ...

  8. 香农编码 哈夫曼编码 费诺编码的比较

    香农编码 哈夫曼编码 费诺编码的比较 文章目录 哈夫曼编码 编码步骤 例子 优点 缺点 费诺编码 编码步骤 例子 优点 缺点 香农编码 编码步骤 例子 优点 缺点 参考 备注:本文除了例子与数据,其他 ...

  9. 香农编码与Huffman编码之间的对比

    目录 一.[上机目的] 二.[环境] 三.[上机原理] 四.[上机内容] 五.设计思路 5.1霍夫曼编码特点 5.2霍夫曼编码原理 5.3二进制哈夫曼编码过程 5.4计算结果 5.5霍夫曼编码的MAT ...

最新文章

  1. 视频|深度相机与应用
  2. 用C语言解“12-24小时制”题
  3. 自制精排 ePub 集、不定期更新(UPDATA-2015-8-2)
  4. OpenGL 期末考试作业
  5. TFS 解除独占锁定
  6. .NET Core 3.0 特性初探:C# 8、WPF、Windows Forms、EF Core
  7. 使用OAuth 2 / OpenID Connect的SSO的Spring Boot 2本机方法
  8. 使用Xtext为Eclipse和IntelliJ开发DSL
  9. oracle 存储过程写文件,Oracle写本地文件
  10. 宽容随和 不失勤恳 充满信心--对工作、生活的一些感悟
  11. arduino 按钮读取_Arduino内置教程-数字-检测按键状态
  12. Linux的巡检命令
  13. java instanceof和isInstance的关系 精析
  14. 安卓10源码开发定制(30)screencap命令源码分析
  15. Windows Server 2008 R2 下载地址
  16. 使用PS把证件照背景变成白色
  17. 往前走吧,管它未来是什么!!
  18. java的h2是什么_什么是H2数据库
  19. Vue + Echart 绘制地图区域标记
  20. java鸡兔同笼:鸡兔同笼,鸡与兔,一共35只,共有94条脚,问,一个鸡有多少只,兔有多少只?

热门文章

  1. Oracle GL - 使用标准程序获取/创建CCID
  2. 总结一些IT项目经理的管理方法与经验
  3. 堡垒机JumpServer(六):内网管理云端服务器
  4. 图片太大,怎么压缩图片大小?
  5. Android的Scroller介绍
  6. SQL SERVER 实用教程(第四版) 实验 1-10 非标准答案
  7. 攻壳机动队中的塔奇克马有灵魂吗?烧脑深度思考,慎点
  8. 第十五周项目二-----用哈希法组织关键字之线性探测法
  9. win7怎么看计算机显卡内存大小,显存,教您怎么看电脑的显存
  10. Photoshop CS6 MAC 中文版破解版 支持Retina屏