热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

香农-范诺算法(Shannon-Fanocoding)原理

和Huffman-Tree一样,Shannon-Fanocoding也是用一棵二叉树对字符进行编码。但在实际操作中呢,Shannon-Fano却没有大用处,这是由于它与Huff

       和Huffman-Tree一样,Shannon-Fano coding也是用一棵二叉树对字符进行编码。但在实际操作中呢,Shannon-Fano却没有大用处,这是由于它与Huffman coding相比,编码效率较低的结果(或者说香农-范诺算法的编码平均码字较大)。但是它的基本思路我们还是可以参考下的。


根据Wikipedia上面的解释,我们来看下香农范诺算法的原理:

Shannon-Fano的树是根据旨在定义一个有效的代码表的规范而建立的。实际的算法很简单:

  1. 对于一个给定的符号列表,制定了概率相应的列表或频率计数,使每个符号的相对发生频率是已知。
  2. 排序根据频率的符号列表,最常出现的符号在左边,最少出现的符号在右边。
  3. 清单分为两部分,使左边部分的总频率和尽可能接近右边部分的总频率和。
  4. 该列表的左半边分配二进制数字0,右半边是分配的数字1。这意味着,在第一半符号代都是将所有从0开始,第二半的代码都从1开始。
  5. 对左、右半部分递归应用步骤3和4,细分群体,并添加位的代码,直到每个符号已成为一个相应的代码树的叶。


示例


香农-范诺编码算法

这个例子展示了一组字母的香浓编码结构(如图a所示)这五个可被编码的字母有如下出现次数:

Symbol A B C D E
Count 15 7 6 6 5
Probabilities 0.38461538 0.17948718 0.15384615 0.15384615 0.12820513

从左到右,所有的符号以它们出现的次数划分。在字母B与C之间划定分割线,得到了左右两组,总次数分别为22,17。 这样就把两组的差别降到最小。通过这样的分割, A与B同时拥有了一个以0为开头的码字, C,D,E的码子则为1,如图b所示。 随后, 在树的左半边,于A,B间建立新的分割线,这样A就成为了码字为00的叶子节点,B的码子01。经过四次分割, 得到了一个树形编码。 如下表所示,在最终得到的树中, 拥有最大频率的符号被两位编码, 其他两个频率较低的符号被三位编码。

符号 A B C D E
编码 00 01 10 110 111


Entropy(熵,平均码字长度): 


Pseudo-code


 1:  begin
 2:     count source units
 3:     sort source units to non-decreasing order
 4:     SF-SplitS
 5:     output(count of symbols, encoded tree, symbols)
 6:     write output
 7:   end
 8:  
 9:  procedure SF-Split(S)
10:  begin
11:     if (|S|>1) then
12:      begin
13:        divide S to S1 and S2 with about same count of units
14:        add 1 to codes in S1
15:        add 0 to codes in S2
16:        SF-Split(S1)
17:        SF-Split(S2)
18:      end
19:  end
想不清楚的朋友可以看下这个网站的模拟程序,很形象,perfect~






香农-范诺算法实现(Shannon-Fano coding implementation in C++)



我们由上面的算法可知,需要迭代地寻找一个最优点,使得树中每个节点的左右子树频率总和尽可能相近。
这里我寻找最优化点用的是顺次查找法,其实呢,我们还可以用二分法(dichotomy)达到更高的效率~


[cpp]  view plain  copy
  1. /************************************************************************/  
  2. /*  File Name: Shanno-Fano.cpp 
  3. *       @Function: Lossless Compression 
  4. @Author: Sophia Zhang 
  5. @Create Time: 2012-9-26 20:20 
  6. @Last Modify: 2012-9-26 20:57 
  7. */  
  8. /************************************************************************/  
  9.   
  10. #include"iostream"  
  11. #include "queue"  
  12. #include "map"  
  13. #include "string"  
  14. #include "iterator"  
  15. #include "vector"  
  16. #include "algorithm"  
  17. #include "math.h"  
  18. using namespace std;  
  19.   
  20. #define NChar 8 //suppose use 8 bits to describe all symbols  
  21. #define Nsymbols 1<  
  22. #define INF 1<<31-1  
  23.   
  24. typedef vector<bool> SF_Code;//8 bit code of one char  
  25. map<char,SF_Code> SF_Dic; //huffman coding dictionary  
  26. int Sumvec[Nsymbols];   //record the sum of symbol count after sorting  
  27.   
  28. class HTree  
  29. {  
  30. public :  
  31.     HTree* left;  
  32.     HTree* right;  
  33.     char ch;  
  34.     int weight;  
  35.   
  36.     HTree(){left = right = NULL; weight=0;ch ='\0';}  
  37.     HTree(HTree* l,HTree* r,int w,char c){left = l; right = r;  weight=w;   ch=c;}  
  38.     ~HTree(){delete left; delete right;}  
  39.     bool Isleaf(){return !left && !right; }  
  40. };  
  41.   
  42. bool comp(const HTree* t1, const HTree* t2)//function for sorting  
  43. {   return (*t1).weight>(*t2).weight;    }  
  44.   
  45. typedef vector TreeVector;  
  46. TreeVector TreeArr;//record the symbol count array after sorting  
  47.   
  48. void Optimize_Tree(int a,int b,HTree& root)//find optimal separate point and optimize tree recursively  
  49. {  
  50.     if(a==b)//build one leaf node  
  51.     {  
  52.         root = *TreeArr[a-1];  
  53.         return;  
  54.     }  
  55.     else if(b-a==1)//build 2 leaf node  
  56.     {  
  57.         root.left = TreeArr[a-1];  
  58.         root.right=TreeArr[b-1];  
  59.         return;  
  60.     }  
  61.     //find optimizing point x  
  62.     int x,minn=INF,curdiff;  
  63.     for(int i=a;i//find the point that minimize the difference between left and right; this can also be implemented by dichotomy  
  64.     {  
  65.         curdiff = Sumvec[i]*2-Sumvec[a-1]-Sumvec[b];  
  66.         if(abs(curdiff)
  67.             x=i;  
  68.             minn = abs(curdiff);  
  69.         }  
  70.         else break;//because this algorithm has monotonicity  
  71.     }  
  72.     HTree*lc = new HTree;   HTree *rc = new HTree;  
  73.     root.left = lc;     root.right = rc;  
  74.     Optimize_Tree(a,x,*lc);  
  75.     Optimize_Tree(x+1,b,*rc);  
  76. }  
  77.   
  78. HTree* BuildTree(int* freqency)//create the tree use Optimize_Tree  
  79. {  
  80.     int i;  
  81.     for(i=0;i//statistic  
  82.     {  
  83.         if(freqency[i])  
  84.             TreeArr.push_back(new HTree (NULL,NULL,freqency[i], (char)i));  
  85.     }  
  86.     sort(TreeArr.begin(), TreeArr.end(), comp);  
  87.     memset(Sumvec,0,sizeof(Sumvec));  
  88.     for(i=1;i<=TreeArr.size();i++)  
  89.         Sumvec[i] = Sumvec[i-1]+TreeArr[i-1]->weight;  
  90.     HTree* root = new HTree;  
  91.     Optimize_Tree(1,TreeArr.size(),*root);  
  92.     return root;  
  93. }  
  94.   
  95. /************************************************************************/  
  96. /* Give Shanno Coding to the Shanno Tree 
  97. /*PS: actually, this generative process is same as Huffman coding 
  98. /************************************************************************/  
  99. void Generate_Coding(HTree* root, SF_Code& curcode)  
  100. {  
  101.     if(root->Isleaf())  
  102.     {  
  103.         SF_Dic[root->ch] = curcode;  
  104.         return;  
  105.     }  
  106.     SF_Code lcode = curcode;  
  107.     SF_Code rcode = curcode;  
  108.     lcode.push_back(false);  
  109.     rcode.push_back(true);  
  110.     Generate_Coding(root->left,lcode);  
  111.     Generate_Coding(root->right,rcode);  
  112. }  
  113.   
  114. int main()  
  115. {  
  116.     int freq[Nsymbols] = {0};  
  117.     char *str = "bbbbbbbccccccaaaaaaaaaaaaaaaeeeeedddddd";//15a,7b,6c,6d,5e  
  118.   
  119.     //statistic character frequency  
  120.     while (*str!='\0')      freq[*str++]++;  
  121.   
  122.     //build tree  
  123.     HTree* r = BuildTree(freq);  
  124.     SF_Code nullcode;  
  125.     Generate_Coding(r,nullcode);  
  126.   
  127.     for(map<char,SF_Code>::iterator it = SF_Dic.begin(); it != SF_Dic.end(); it++) {    
  128.         cout<<(*it).first<<'\t';    
  129.         std::copy(it->second.begin(),it->second.end(),std::ostream_iterator<bool>(cout));    
  130.         cout<
  131.     }    
  132. }  


Result:

以上面图中的统计数据为例,进行编码。

符号 A B C D E
计数 15 7 6 6 5




Reference:

  1. Shannon-Fano coding. Wikipedia, the free encyclopedia
  2. Claude Elwood Shannon. Wikipedia, the free encyclopedia.
  3. C. E. Shannon: A Mathematical Theory of Communication. The Bell System Technical Journal, Vol. 27, July, October, 1948.
  4. C. E. Shannon: Prediction and Entropy of Printed English. The Bell System Technical Journal, Vol. 30, 1951.
  5. C. E. Shannon: Communication Theory of Secrecy Systems. The Bell System Technical Journal, Vol. 28, 1949.
  6. http://www.stringology.org/DataCompression/sf/index_en.html

推荐阅读
  • 本文由编程笔记#小编为大家整理,主要介绍了logistic回归(线性和非线性)相关的知识,包括线性logistic回归的代码和数据集的分布情况。希望对你有一定的参考价值。 ... [详细]
  • 阿里Treebased Deep Match(TDM) 学习笔记及技术发展回顾
    本文介绍了阿里Treebased Deep Match(TDM)的学习笔记,同时回顾了工业界技术发展的几代演进。从基于统计的启发式规则方法到基于内积模型的向量检索方法,再到引入复杂深度学习模型的下一代匹配技术。文章详细解释了基于统计的启发式规则方法和基于内积模型的向量检索方法的原理和应用,并介绍了TDM的背景和优势。最后,文章提到了向量距离和基于向量聚类的索引结构对于加速匹配效率的作用。本文对于理解TDM的学习过程和了解匹配技术的发展具有重要意义。 ... [详细]
  • 本文介绍了闭包的定义和运转机制,重点解释了闭包如何能够接触外部函数的作用域中的变量。通过词法作用域的查找规则,闭包可以访问外部函数的作用域。同时还提到了闭包的作用和影响。 ... [详细]
  • 生成式对抗网络模型综述摘要生成式对抗网络模型(GAN)是基于深度学习的一种强大的生成模型,可以应用于计算机视觉、自然语言处理、半监督学习等重要领域。生成式对抗网络 ... [详细]
  • 在Android开发中,使用Picasso库可以实现对网络图片的等比例缩放。本文介绍了使用Picasso库进行图片缩放的方法,并提供了具体的代码实现。通过获取图片的宽高,计算目标宽度和高度,并创建新图实现等比例缩放。 ... [详细]
  • CSS3选择器的使用方法详解,提高Web开发效率和精准度
    本文详细介绍了CSS3新增的选择器方法,包括属性选择器的使用。通过CSS3选择器,可以提高Web开发的效率和精准度,使得查找元素更加方便和快捷。同时,本文还对属性选择器的各种用法进行了详细解释,并给出了相应的代码示例。通过学习本文,读者可以更好地掌握CSS3选择器的使用方法,提升自己的Web开发能力。 ... [详细]
  • 本文介绍了Java工具类库Hutool,该工具包封装了对文件、流、加密解密、转码、正则、线程、XML等JDK方法的封装,并提供了各种Util工具类。同时,还介绍了Hutool的组件,包括动态代理、布隆过滤、缓存、定时任务等功能。该工具包可以简化Java代码,提高开发效率。 ... [详细]
  • 计算机存储系统的层次结构及其优势
    本文介绍了计算机存储系统的层次结构,包括高速缓存、主存储器和辅助存储器三个层次。通过分层存储数据可以提高程序的执行效率。计算机存储系统的层次结构将各种不同存储容量、存取速度和价格的存储器有机组合成整体,形成可寻址存储空间比主存储器空间大得多的存储整体。由于辅助存储器容量大、价格低,使得整体存储系统的平均价格降低。同时,高速缓存的存取速度可以和CPU的工作速度相匹配,进一步提高程序执行效率。 ... [详细]
  • 欢乐的票圈重构之旅——RecyclerView的头尾布局增加
    项目重构的Git地址:https:github.comrazerdpFriendCircletreemain-dev项目同步更新的文集:http:www.jianshu.comno ... [详细]
  • 开源Keras Faster RCNN模型介绍及代码结构解析
    本文介绍了开源Keras Faster RCNN模型的环境需求和代码结构,包括FasterRCNN源码解析、RPN与classifier定义、data_generators.py文件的功能以及损失计算。同时提供了该模型的开源地址和安装所需的库。 ... [详细]
  • 本文由编程笔记#小编为大家整理,主要介绍了源码分析--ConcurrentHashMap与HashTable(JDK1.8)相关的知识,希望对你有一定的参考价值。  Concu ... [详细]
  • 求解连通树的最小长度及优化
    本文介绍了求解连通树的最小长度的方法,并通过四边形不等式进行了优化。具体方法为使用状态转移方程求解树的最小长度,并通过四边形不等式进行优化。 ... [详细]
  • 判断编码是否可立即解码的程序及电话号码一致性判断程序
    本文介绍了两个编程题目,一个是判断编码是否可立即解码的程序,另一个是判断电话号码一致性的程序。对于第一个题目,给出一组二进制编码,判断是否存在一个编码是另一个编码的前缀,如果不存在则称为可立即解码的编码。对于第二个题目,给出一些电话号码,判断是否存在一个号码是另一个号码的前缀,如果不存在则说明这些号码是一致的。两个题目的解法类似,都使用了树的数据结构来实现。 ... [详细]
  • 在加载一个第三方厂商的dll文件时,提示“找不到指定模块,加载失败”。由于缺乏必要的技术支持,百思不得期间。后来发现一个有用的工具 ... [详细]
  • 文章目录题目:二叉搜索树中的两个节点被错误地交换。基本思想1:中序遍历题目:二叉搜索树中的两个节点被错误地交换。请在不改变其结构的情况下 ... [详细]
author-avatar
手机用户2502905157
这个家伙很懒,什么也没留下!
Tags | 热门标签
RankList | 热门文章
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有