前言

  LZ77算法是无损压缩算法,由以色列人Abraham Lempel发表于1977年。LZ77是典型的基于字典的压缩算法,现在很多压缩技术都是基于LZ77。鉴于其在数据压缩领域的地位,本文将结合图片和源码详细介绍其原理。

原理介绍:

  首先介绍几个专业术语。

  1.lookahead buffer(不知道怎么用中文表述,暂时称为待编码区):

  等待编码的区域

  2. search buffer:

  已经编码的区域,搜索缓冲区

  3.滑动窗口:

  指定大小的窗,包含“搜索缓冲区”(左) + “待编码区”(右)

  

  接下来,介绍具体的编码过程:

  为了编码待编码区, 编码器在滑动窗口的搜索缓冲区查找直到找到匹配的字符串。匹配字符串的开始字符串与待编码缓冲区的距离称为“偏移值”,匹配字符串的长度称为“匹配长度”。编码器在编码时,会一直在搜索区中搜索,直到找到最大匹配字符串,并输出(o, l ),其中o是偏移值, l是匹配长度。然后窗口滑动l,继续开始编码。如果没有找到匹配字符串,则输出(0, 0, c),c为待编码区下一个等待编码的字符,窗口滑动“1”。算法实现将类似下面的:

  while( lookAheadBuffer not empty )
{
    get a pointer (position, match) to the longest match 
    in the window for the lookAheadBuffer;

output a (position, length, char());
    shift the window length+1 characters along;
}

  主要步骤为:

  1.设置编码位置为输入流的开始

  2.在滑窗的待编码区查找搜索区中的最大匹配字符串

  3.如果找到字符串,输出(偏移值, 匹配长度), 窗口向前滑动“匹配长度”

  4.如果没有找到,输出(0, 0, 待编码区的第一个字符),窗口向前滑动一个单位

  5.如果待编码区不为空,回到步骤2

  描述实在是太复杂,还是结合实例来讲解吧

实例:

  现在有字符串“AABCBBABC”,现在对其进行编码。

  一开始,窗口滑入如图位置

  

  由图可见,待编码缓冲区有“AAB”三个字符,此时搜索缓冲区还是空的。所以编码第一个字符,由于搜索区为空,故找不到匹配串,输出(0,0, A),窗口右移一个单位,如下图

  

  此时待编码区有“ABC”。开始编码。最先编码"A",在搜索区找到"A"。由于没有超过待编码区,故开始编码"AB",但在搜索区没有找到匹配字符串,故无法编码。因此只能编码"A"。

输出(1, 1)。即为相对于待编码区,偏移一个单位,匹配长度为1。窗口右滑动匹配长度,即移动1个单位。如下图

  

  一样,没找到,输出(0, 0, B),右移1个单号,如下图

  

  输出(0, 0, C),右移1个单位,如下图

  

  输出(2, 1),右移1个单位,如下图

  

  输出(3, 1), 右移1个单位,如下图

  

  开始编码"A",在搜索缓冲区查找到匹配字符串。由于待编码缓冲区没有超过,继续编码。开始编码"AB",也搜索到。不要停止,继续编码“ABC”,找到匹配字符串。由于继续编码,则超过了窗口,故只编码“ABC”,输出(5, 3),偏移5,长度3。右移3个单位,如下图

  

  此时待编码缓冲区为空,停止编码。

  最终输出结果如下

  

python代码实现:

  

  1. class Lz77:
  2. def __init__(self, inputStr):
  3. self.inputStr = inputStr #输入流
  4. self.searchSize = 5 #搜索缓冲区(已编码区)大小
  5. self.aheadSize = 3 #lookAhead缓冲区(待编码区)大小
  6. self.windSpiltIndex = 0 #lookHead缓冲区开始的索引
  7. self.move = 0
  8. self.notFind = -1 #没有找到匹配字符串
  9.  
  10. #得到滑动窗口的末端索引
  11. def getWinEndIndex(self):
  12. return self.windSpiltIndex + self.aheadSize
  13.  
  14. #得到滑动窗口的始端索引
  15. def getWinStartIndex(self):
  16. return self.windSpiltIndex - self.searchSize
  17.  
  18. #判断lookHead缓冲区是否为空
  19. def isLookHeadEmpty(self):
  20. return True if self.windSpiltIndex + self.move> len(self.inputStr) - 1 else False
  21.  
  22. def encoding(self):
  23. step = 0
  24. print("Step Position Match Output")
  25. while not self.isLookHeadEmpty():
  26. #1.滑动窗口
  27. self.winMove()
  28. #2. 得到最大匹配串的偏移值和长度
  29. (offset, matchLen) = self.findMaxMatch()
  30. #3.设置窗口下一步需要滑动的距离
  31. self.setMoveSteps(matchLen)
  32. if matchLen == 0:
  33. #匹配为0,说明无字符串匹配,输出下一个需要编码的字母
  34. nextChar = self.inputStr[self.windSpiltIndex]
  35. result = (step, self.windSpiltIndex, '-', '(0,0)' + nextChar)
  36. else:
  37. result = (step, self.windSpiltIndex, self.inputStr[self.windSpiltIndex - offset: self.windSpiltIndex - offset + matchLen], '(' + str(offset) + ',' + str(matchLen) + ')')
  38. #4.输出结果
  39. self.output(result)
  40. step = step + 1 #仅用来设置第几步
  41.  
  42. #滑动窗口(移动分界点)
  43. def winMove(self):
  44. self.windSpiltIndex = self.windSpiltIndex + self.move
  45.  
  46. #寻找最大匹配字符并返回相对于窗口分界点的偏移值和匹配长度
  47. def findMaxMatch(self):
  48. matchLen = 0
  49. offset = 0
  50. minEdge = self.minEdge() + 1 #得到编码区域的右边界
  51. #遍历待编码区,寻找最大匹配串
  52. for i in range(self.windSpiltIndex + 1, minEdge):
  53. #print("i: %d" %i)
  54. offsetTemp = self.searchBufferOffest(i)
  55. if offsetTemp == self.notFind:
  56. return (offset, matchLen)
  57. offset = offsetTemp #偏移值
  58.  
  59. matchLen = matchLen + 1 #每找到一个匹配串,加1
  60.  
  61. return (offset, matchLen)
  62.  
  63. #入参字符串是否存在于搜索缓冲区,如果存在,返回匹配字符串的起始索引
  64. def searchBufferOffest(self, i):
  65. searchStart = self.getWinStartIndex()
  66. searchEnd = self.windSpiltIndex
  67. #下面几个if是处理开始时的特殊情况
  68. if searchEnd < 1:
  69. return self.notFind
  70. if searchStart < 0:
  71. searchStart = 0
  72. if searchEnd == 0:
  73. searchEnd = 1
  74. searchStr = self.inputStr[searchStart : searchEnd] #搜索区字符串
  75. findIndex = searchStr.find(self.inputStr[self.windSpiltIndex : i])
  76. if findIndex == -1:
  77. return -1
  78. return len(searchStr) - findIndex
  79.  
  80. #设置下一次窗口需要滑动的步数
  81. def setMoveSteps(self, matchLen):
  82. if matchLen == 0:
  83. self.move = 1
  84. else:
  85. self.move = matchLen
  86.  
  87. def minEdge(self):
  88. return len(self.inputStr) if len(self.inputStr) - 1 < self.getWinEndIndex() else self.getWinEndIndex() + 1
  89.  
  90. def output(self, touple):
  91. print("%d %d %s %s" % touple)
  92.  
  93. if __name__ == "__main__":
  94. lz77 = Lz77("AABCBBABC")
  95. lz77.encoding()

  只是简单的写了下,没有过多考虑细节,请注意,这不是最终的代码,只是用来阐述原理,仅供参考。输出结果就是上面的输出(格式由于坑爹的博客园固定样式,代码位置有偏移,请注意)

参考文章:

  http://msdn.microsoft.com/en-us/library/ee916854.aspx

  http://en.wikipedia.org/wiki/LZ77_and_LZ78

  http://cs.stanford.edu/people/eroberts/courses/soco/projects/2000-01/data-compression/lossless/lz77/algorithm.htm

  以上几篇文章都是很好的讲解LZ77原理的,大家有兴趣的可以参考下。由于国内介绍该算法的比较少,故这些英文文章帮助还是挺大的。

  

LZ77压缩算法编码原理详解(结合图片和简单代码)的更多相关文章

  1. crawler_URL编码原理详解

    经常写爬虫的童鞋,难免要处理含有中文的url,大部分时间,都知道url_encode,各个语言也都有支持,今天简单整理下原理,供大家科普 1.特征: 如果URL中含有非ASCII字符的话, 浏览器会对 ...

  2. [转]utf8编码原理详解

    from : http://blog.csdn.net/baixiaoshi/article/details/40786503 很久很久以前,有一群人,他们决定用8个可以开合的晶体管来组合成不同的状态 ...

  3. 块级格式化上下文(block formatting context)、浮动和绝对定位的工作原理详解

    CSS的可视化格式模型中具有一个非常重要地位的概念——定位方案.定位方案用以控制元素的布局,在CSS2.1中,有三种定位方案——普通流.浮动和绝对定位: 普通流:元素按照先后位置自上而下布局,inli ...

  4. SSL/TLS 原理详解

    本文大部分整理自网络,相关文章请见文后参考. SSL/TLS作为一种互联网安全加密技术,原理较为复杂,枯燥而无味,我也是试图理解之后重新整理,尽量做到层次清晰.正文开始. 1. SSL/TLS概览 1 ...

  5. [No0000126]SSL/TLS原理详解与WCF中的WS-Security

    SSL/TLS作为一种互联网安全加密技术 1. SSL/TLS概览 1.1 整体结构 SSL是一个介于HTTP协议与TCP之间的一个可选层,其位置大致如下: SSL:(Secure Socket La ...

  6. NFS原理详解

    NFS原理详解 摘自:http://atong.blog.51cto.com/2393905/1343950 2013-12-23 12:17:31 标签:linux NFS nfs原理详解 nfs搭 ...

  7. Netty学习——服务器端代码和客户端代码 原理详解

    服务器端代码和客户端代码 原理详解:(用到的API) 0.Socket 连接服务器端的套接字 1.TcompactProtocol   协议层2.TFrameTransport   传输层3.THsh ...

  8. I2C 基础原理详解

    今天来学习下I2C通信~ I2C(Inter-Intergrated Circuit)指的是 IC(Intergrated Circuit)之间的(Inter) 通信方式.如上图所以有很多的周边设备都 ...

  9. Zigbee组网原理详解

    Zigbee组网原理详解 来源:互联网 作者:佚名2015年08月13日 15:57   [导读] 组建一个完整的zigbee网状网络包括两个步骤:网络初始化.节点加入网络.其中节点加入网络又包括两个 ...

随机推荐

  1. Deep learning:五十一(CNN的反向求导及练习)

    前言: CNN作为DL中最成功的模型之一,有必要对其更进一步研究它.虽然在前面的博文Stacked CNN简单介绍中有大概介绍过CNN的使用,不过那是有个前提的:CNN中的参数必须已提前学习好.而本文 ...

  2. JS去重及字符串奇数位小写转大写

    面试中经常会考到数组的去重.作为一名合格的前端开发者,不知道几种去重方法是在不应该.废话不多说直接开撸-- 一.indexOf()方法 实现思路:使用indexOf()方法来判断新数组中是否有这个值, ...

  3. 使用代码向一个普通的类注入Spring的实例

    转载请在页首注明作者与原文地址 一:应用场景 什么是普通的类,就是没有@Controller,@Service,@Repository,@Component等注解修饰的类,同时xml文件中,也没有相应 ...

  4. GitHub更新自己Fork的项目

    转自:http://www.tuicool.com/articles/MzMJre github上有个功能叫fork,可以将别人的工程复制到自己账号下.这个功能很方便,但其有一个缺点是:当源项目更新后 ...

  5. Http协议相关内容

    http协议概述 HTTP是hypertext transfer protocol(超文本传输协议)的简写,它是TCP/IP协议的一个应用层协议,用于定义浏览器与WEB服务器之间交换数据的过程. 客户 ...

  6. 学C#之设计模式系列笔记(1)策略模式

    一.借鉴说明 1.<Head First Design Patterns>(中文名<深入浅出设计模式>) 2.维基百科,策略模式,https://zh.wikipedia.or ...

  7. jQuery图片延迟加载

    这里延迟加载的意思是,拖动滚动条时,在图片出现在浏览器显示区域后才加载显示.在这之前全部图片都由一张图片代替.节省流量,减轻服务器负担. 效果展示 http://hovertree.com/texia ...

  8. 【C#公共帮助类】 WebHelper帮助类

    如果你是一个新手,如果你刚接触MVC,如果你跟着置顶的那个项目,我们肯定会用到这里面的几个帮助类 它们都在Common类库下,大家一定要记住要点:取其精华去其糟粕,切勿拿来主义~ Applicatio ...

  9. spider RPC插件化体系

    为了满足灵活扩展的需要,spider支持灵活的自定义插件扩展,从功能上来说,插件和过滤器的差别在于过滤器不会阻止请求的执行同时对于主程序不会有API上的影响(比如servlet 过滤器和监听器)(最多 ...

  10. 2016年6月份那些最实用的 jQuery 插件专辑

    jQuery 是一个快速.流行的 JavaScript 库,jQuery 用于文档处理.事件处理.动画和 Ajax 交互非常简单,学习曲线也很平坦.2016年6月的 jQuery 插件专辑里,我们选择 ...