>>分享数据结构和算法相关的知识和技术 书籍支持  卫琴直播  品书摘要  在线测试  资源下载  联系我们
发表一个新主题 开启一个新投票 回复文章 您是本文章第 19593 个阅读者 刷新本主题
 * 贴子主题:  有关图片的LZW算法的原理 回复文章 点赞(0)  收藏  
作者:flybird    发表时间:2020-03-15 15:09:44     消息  查看  搜索  好友  邮件  复制  引用

    
     最近突然对 GIF 图片感兴趣了,然后简单研究了一下,颇有一番心得,不过这篇文章不谈 GIF 图片,而是要说一说 GIF 图片技术中用到的一种压缩算法,LZW 这种压缩算法属于无损压缩的一种,我看了好久在维基百科或者是百度百科上都没看明白,特别对一上来就只扑代码的介绍方式很反感。最后在 【这个人的博客】里看懂了压缩方式,解压方式自己又摸索了下,好不容易理解了,分享给大家,看大家能不能在 15分钟 内理解。

压缩的核心思想是用记号来代替重复出现的字符串,举个栗子:  AABAA,这个字符串中   AA 出现了2次,那么我可以用   1 这个数字来代替   AA ,那么这个字符串压缩后就成了   1B1,当然,这不是无损压缩,因为   1 是什么,在解压的时候是不知道的,我们可以简单的把记号和原串的对应表附在字符串后面,这样解压的时候就可以查询解压了,比如   1B1:1,AA 懂点程序的都能明白,确实可以根据这个串来还原,但是这种附一个对应表的做法怎么看都很累赘。LZW 给出了一种方式,让我们的这种做法显得不那么累赘。让我们可以边解压,边翻译出记号对应的原始串。

那么它是怎么做到的呢?

用字符串的压缩来举例,为了方便查看,我把原始串和压缩之后的结果串放在一起,这样可以让读者自己检测:

原始串:  ABABABABBBABABAA

压缩串:  AB02B43AA

这样一个字符串,我们来用 LZW 压缩看看。

首先我们要明白,我们的目的其实是为了找出重复的串,然后用我们自己定义的记号替换掉。

好了,让我们模拟程序的过程,来一步一步走。

  先看压缩过程。

1、先读入一个字符,  A

2、再读入一个字符,  B

3、LZW算法以两个字符为检查单元(为什么不能更多?)这个时候判断   AB 有没有出现过,我们发现没有出现过,那么意味着我们可以用一个记号来代替   AB ,比如用   0 代替   AB。然后我们记录下这个记号和它对应的字符串,当然,这个记录是个临时的,最后不需要我们整合进压缩的字符串中。

4、我们把这两个字母中的前一个放入压缩串中,后一个握在手上,等接下来读取的一个字符,拼成两个字母的检查单元。

5、读取下一个字符,  A。这个时候我们手上有   B ,和   A 拼起来成为   BA,判断   BA 没有出现过,用   1 代替   BA。同样记录对应关系,然后把这个检查单元的前一个字符   B 放入压缩串,后一个字符   A 握在手上等下一个字符。

6、读取下一个字符,  B。拼起来是   AB  AB 是出现过的,我们还用   0 代替了这个串,那么这里我们的行为是用   0 代替手上的   AB,然后不做其他的事情。现在我们手上握着的是   0 了。

7、读取下一个字符,  A。拼起来是   0A  0 虽然是我们自己定义的记号,但是也可以看做字符,那么   0A 也没有出现过,我们用   2 代替   0A,记录下这个对应关系,然后把   0 放进压缩串,手上握着   A

8、后面的过程就是如上反复了,最后我们就能够得到上面我给出的压缩串。

   仔细分析一下压缩过程就发现,LZW算法的压缩过程,就是把所有出现的新串都用一个记号代替,因为记号代替的串中也可以包含别的记号,所以一个记号迭代翻译成原始字符就可能指代了很长的一串,由此来达到压缩效果。

     然后我们看解压过程。解压正好和压缩是相反的,我们只有压缩串了。

压缩串:  AB02B43AA

原始串:  ABABABABBBABABAA

因为我们没有附加对应表的信息,所以聪明人已经想到了,想要恢复原始串,唯一可行的方式就是跟压缩时一样,边读边生成对应表,才可能不需要附加对应表。

我们来看解压过程。

1、读取一个字符,  A

2、读取一个字符,  B

3、为了自己解释自己,那么必须重复压缩的过程,我们也需要生成对应表,同样使用压缩的时候的方式,用两个字符作为检查单元。这里我们检查 AB 没有出现过,那么用   0 代替   AB,这就是我们生成的第一个对应关系,跟压缩一样,记录下来,然后把   A 放入要生成的原始串,手上握着   B

4、读取下一个字符,  0。你突然意识到,你居然知道   0 是什么,对!但不要慌,先不要慌着替换,因为我们需要兼顾后面的对应关系,这里我们来看   B0 ,这个串没有出现,所以这里还有一个对应关系,回想压缩的过程,我们发现,我们记录的对应关系表里,记号对应的字符串都是以正常字符结尾的,也就是结尾字符不会是另一个记号,而这里   B0 中结尾的   0 明显是个记号,所以这里的新记号   1 肯定不是   B0。那么   1 是什么呢?再看看压缩过程,我们都是用两个字符的检查单元的前一个加后一个作为新记号对应的字符串的,而后一个字符肯定是正常字符,那么这里   1 肯定是以   B 开头,后一个字符是   0 的第一个字符。我们查询已经生成了一个对应关系的表可以知道   0  AB ,那么   1 就是   BA

5、记录下   1  BA,依然把   B 放入原始串,手上握着   0

6、读取下一个字符,  2。这个时候看到   02 ,两个都是记号,不要慌,首先,  02 这个检查单元没有出现过,那么这里必然有新的记号生成。这个记号显然就是   2 本身。仿造第5条的方法,我们知道   2 肯定是以   0 开头,后一个字符是   2 的第一个字符。这不成死循环了吗?我们压根不知道   2 是什么?但是,注意看,我们不需要知道   2 是什么,我们只要知道   2 的第一个字符是什么就可以了,我刚才不是说   2 肯定是以   0 开头吗?那么   2 的第一个字符明显就是   0 的第一个字符啊!而   0 我们是知道的,豁然开朗了,  0  AB ,那么   2 就是   0A 了。

6、记录下   2  0A,把   0 放入原始串,手上握着   2。当然,原始串中不能有记号,我们用   AB 换掉   0 放入原始串。

7、后面的过程也是如上反复了,最难理解的地方就在于第6条,出现一个我们不认识的记号的时候。你仔细品味一下就能理解了。你牢记一点就行了,但凡遇到新的检查单元,就必然存在新的记号出现。

   理解了 LZW,我有一个问题,也是我在上面压缩过程第3条中标注的,为什么要以2个字符为检查单元,能不能更多呢?首先我们知道肯定是不能无限多的,比如如果字符串有10个字符,我们正好以10个字符作为检查单元,那么这10个字符压缩后肯定只有一个记号,那解压的时候手上就这一个记号,回天乏术,无法重构原始串。那么能不能多一点点呢?比如3个字符长度作为检查单元?关于这个问题,我仔细思考了一番,没有想到好的办法,虽然我无法做到,但我没有从原理上去给出解释,所以这里也留给大家自己思考吧。

--------------------------
原文链接:https://blog.51cto.com/rangercyh/1554578

程序猿的技术大观园:www.javathinker.net



[这个贴子最后由 flybird 在 2020-03-20 12:11:26 重新编辑]
  Java面向对象编程-->操作符
  JavaWeb开发-->在Web应用中访问Web服务
  JSP与Hibernate开发-->Spring、JPA与Hibernate的整合
  Java网络编程-->RMI框架
  精通Spring-->创建综合购物网站应用
  Vue3开发-->创建综合购物网站应用
  无向图的最短路径求解算法之——Dijkstra算法
  图像基本处理算法的简单实现
  HAProxy 之 算法介绍
  java 通配符的应用范例, java 排序算法
  Java 选择排序算法
  PageRank算法
  基于SQL的数据库算法研究
  Haproxy支持的调度算法
  Citrix Netscaler负载均衡算法
  活动安排问题(贪心算法)
  天干地支算法
  Java算法题:有关100阶楼梯的算法
  SHA-1算法详解
  微软面试题:买卖股票的最佳时机
  浅谈常见的七种加密算法及实现
  更多...
 IPIP: 已设置保密
楼主      
1页 0条记录 当前第1
发表一个新主题 开启一个新投票 回复文章


中文版权所有: JavaThinker技术网站 Copyright 2016-2026 沪ICP备16029593号-2
荟萃Java程序员智慧的结晶,分享交流Java前沿技术。  联系我们
如有技术文章涉及侵权,请与本站管理员联系。