+ -
当前位置:首页 → 问答吧 → 基于静态huffman编码的压缩-PHP语言实现

基于静态huffman编码的压缩-PHP语言实现

时间:2010-07-29

来源:互联网



名词解释:哈夫曼编码(Huffman Coding)是一种编码方式,哈夫曼编码是可变字长编码(VLC)的一种。该方法依据字符出现概率来构造异字头的平均长度最短的码字,有时称之为最佳编码,一般就叫作Huffman编码。

实现过程:


1.计算每个字符在字符串中出现的频率作为构建huffman树的权重

2.构建huffman树

3.建立每个字符对应的编码表

4.重建字符串编码,既压缩字符串


5.解压时根据先前的huffman树和字符位长度还原字符串


  1. <?php   
  2. /**
  3. 基于静态huffman编码的压缩[PHP语言实现]
  4. author:        lajabs
  5. email:        aGl0dHlvQGdtYWlsLmNvbQ==

  6. 本文以PHP作为描述语言较详细讲解huffman树原理及应用
  7. 因保证程序可读性,故不做优化.

  8. */



  9. class huffman
  10. {
  11.         /**
  12.          * 压缩入口
  13.          * $str:待压缩的字符串
  14.          */
  15.         public function encode($str)
  16.         {
  17.                 $len=strlen($str);
  18.                 //计算每个字符权重值(出现的频度)<这边可以做成概率表>
  19.                 for($i=0;$i<$len;$i++)        $array[ord($str{$i})]++;

  20.                 $HuffmanArray=array();
  21.                 asort($array);
  22.                 /**
  23.                  * 构造huffman树,时间复杂度O(nlogn)
  24.                  * 选择两个使用频率较小<字符在字符串中出现的次数>的结点合并生成出一个树
  25.                  */
  26.                 while ($item1 = each($array))
  27.                 {
  28.                         $item2 = each($array);
  29.                         //构建huffman树
  30.                         $this->creat_tree($item1,$item2,$array,$HuffmanArray);
  31.                         //反复排序<优化这步可在构造树时用插入排序算法完成>
  32.                         asort($array);
  33.                 }


  34.                 $HuffmanArray=array_shift($HuffmanArray);
  35.                 //构建编码表<这步可优化为构建树时一同生成>
  36.                 $tab=null;
  37.                 $code_tab=$this->creat_tab($HuffmanArray,$tab);
  38.                 //压缩&转换整个字符串为二进制表达式
  39.                 $binary=null;
  40.                 for($i=0;$i<$len;$i++)        $binary.=$tab[ord($str{$i})];        
  41.                 //转化为压缩后的字符串
  42.                 $code=$this->encode_bin($binary);
  43.                 //静态huffman编码算法压缩后需保留huffman树
  44.                 return array('tree'=>$HuffmanArray,'len'=>strlen($binary),'code'=>$code);
  45.         }

  46.         /**
  47.          * 解压缩入口
  48.          * $huffman:解压所使用的huffman树
  49.          * $str:被压缩的字符
  50.          * $blen:压缩前的位长度
  51.          */
  52.         public function decode($huffman,$str,$blen)
  53.         {
  54.                 $len=strlen($str);
  55.                 $binary=null;
  56.                 //将编码解为二进制表达式
  57.                 for($i=0;$i<$len;$i++)        
  58.                 $binary.=str_pad(base_convert(ord($str{$i}),10,2),8,'0',STR_PAD_LEFT);
  59.                 //去除补码
  60.                 $binary=substr($binary,0,$blen);
  61.                 //从hufman树中配比相应的编码
  62.                 return $this->decode_tree($binary,$huffman,$huffman);
  63.         }

  64.         /**
  65.          * 将压缩后的二进制表达式再转为字符串
  66.          * $binary:二进制表达式字串
  67.          */
  68.         private function encode_bin($binary)
  69.         {
  70.                 $len=strlen($binary);
  71.                 //二进制转字符需要整8位,不足8位补0
  72.                 $blen=$len+8-$len%8;
  73.                 $binary=str_pad($binary,$blen,'0');
  74.                 $encode=null;
  75.                 //每8位转为一个字符
  76.                 for($i=7;$i<$blen;$i+=8)
  77.                 {
  78.                         $frag=substr($binary,$i-7,8);
  79.                         $encode.=chr(base_convert($frag,2,10));
  80.                 }
  81.                 return $encode;
  82.         }

  83.         /**
  84.          * 构造huffman树,使用贪婪算法选择最小的两个元素作为树的子节点
  85.          * $item1:权重最小的元素1
  86.          * $item2:权重次小的元素2
  87.          * $array:所有字符出现次数表<权重表>
  88.          * $HuffmanArray:保存生成的huffman树结构
  89.          */
  90.         private function creat_tree($item1,$item2,&$array,&$HuffmanArray)
  91.         {
  92.                 list($k,$v)=$item1;
  93.                 list($k2,$v2)=$item2;
  94.                 //假设当前树的左右节点为空节点
  95.                 $c1=$k;
  96.                 $c2=$k2;
  97.                 //判断两个元素若为树则直接作为节点并入主树
  98.                 if(isset($HuffmanArray[$k2]))
  99.                 {
  100.                         $c2=$HuffmanArray[$k2];        
  101.                         unset($HuffmanArray[$k2]);
  102.                 }
  103.                 if(isset($HuffmanArray[$k]))
  104.                 {
  105.                         $c1=$HuffmanArray[$k];
  106.                         unset($HuffmanArray[$k]);
  107.                 }
  108.                 //设置树结点权值
  109.                 $array[$k2]=$v+$v2;                                                        
  110.                 //合并节点后删除元素
  111.                 unset($array[$k]);
  112.                 //合并到huffman树中
  113.                 $HuffmanArray[$k2]=array(0=>$c1,1=>$c2);        
  114.         }


  115.         /**
  116.          * 广度优先遍历树,得到所有原字符对应的二进制表达式<01010...>
  117.          * $tree:已经构建好的huffman树
  118.          * $tab:编码表,保存所有字符对应的编码
  119.          * $a0:左遍历树的路径<11010...>
  120.          * $a1:右遍历树的路径
  121.          */
  122.         private function creat_tab($tree,&$tab,$a0=null,$a1=null)
  123.         {
  124.                 if($tree==null) return;
  125.                 //遍历左右子树
  126.                 foreach($tree as $node=>$ctree)
  127.                 {
  128.                         if(is_array($ctree))
  129.                         {
  130.                                 //判断未到达叶子节点时再向下遍历
  131.                                 $this->creat_tab($ctree,$tab,$a0.$node,$a1.$node);
  132.                         }
  133.                         else
  134.                         {
  135.                                 //遍历到叶子节点<原字符ascii码>时的所有路径,既二进制表达式,下同
  136.                                 $tab[$ctree]=${'a'.$node}.$node;
  137.                         }
  138.                 }
  139.         }

  140.         /**
  141.          * 使用进制表达式深度优先遍历树,0为左子树,1为右子树,而到根节点,即为二进制表达式所指向的原字符
  142.          * $binary:二进制表达式字串
  143.          * $huffman:huffman树
  144.          * $tree:当前所遍历的子树
  145.          * $i:指向二进制表达式字串的<指针>
  146.          * $code:解码后的字符串
  147.          */
  148.         private function decode_tree($binary,$huffman,$tree,$i=0,$code=null)
  149.         {
  150.                 $lr=$binary{$i};
  151.                 //遍历完成
  152.                 if($lr==null) return $code;
  153.                 //判断是否到根节点,根节点既为二进制表达式对应的原字符ascii码
  154.                 if(is_array($tree[$lr]))
  155.                 {
  156.                         //继续向下遍历子树
  157.                         return $this->decode_tree($binary,$huffman,$tree[$lr],$i+1,$code);
  158.                 }
  159.                 else
  160.                 {
  161.                         //将二进制表达式解码为原字符
  162.                         $code.=chr($tree[$lr]);
  163.                         return $this->decode_tree($binary,$huffman,$huffman,$i+1,$code);
  164.                 }
  165.         }
  166. }
  167. ?>
复制代码
测试代码:
  1. $str='
  2. In computer science and information theory, Huffman coding is an entropy encoding algorithm used for lossless data compression. The term refers to the use of a variable-length code table for encoding a source symbol (such as a character in a file) where the variable-length code table has been derived in a particular way based on the estimated probability of occurrence for each possible value of the source symbol. It was developed by David A. Huffman while he was a Ph.D. student at MIT, and published in the 1952 paper "A Method for the Construction of Minimum-Redundancy Codes".
  3. ';

  4. $huffman=new huffman();
  5. $obj=$huffman->encode($str);
  6. echo '压缩前的编码长度:',strlen($str),"\n";
  7. echo '压缩后的编码:',"\n";
  8. var_dump($obj['code']);
  9. echo '解压后的字符:',$huffman->decode($obj['tree'],$obj['code'],$obj['len']);
复制代码
测试结果:压缩前的编码长度:587压缩后的编码:string(330) "sp閉h颚�6鵞+王d挓吷s霒zk洚磗脎|t�*�;娳9蹴?�>楏4O3 5 F凣rRuJ解压后的字符:In computer science and information theory, Huffman coding is an entropy encoding algorithm used for lossless data compression. The term refers to the use of a variable-length code table for encoding a source symbol (such as a character in a file) where the variable-length code table has been derived in a particular way based on the estimated probability of occurrence for each possible value of the source symbol. It was developed by David A. Huffman while he was a Ph.D. student at MIT, and published in the 1952 paper "A Method for the Construction of Minimum-Redundancy Codes".

作者: bs   发布时间: 2010-07-29

牛,学习了,感谢分享!

作者: 7758658   发布时间: 2010-07-29

膜拜..

但是php写算法效率真不行..

作者: TankMe   发布时间: 2010-07-29

似乎有点问题,用楼主的测试代码测试了出现:
Fatal error: Maximum function nesting level of '100' reached, aborting! in D:\wamp\www\test\7-29.php on line 168

递归层数超了100,函数栈爆了

作者: 7758658   发布时间: 2010-07-29