Algorithm 生成';n';二进制前缀码
A是一组代码,因此没有代码是另一个代码的前缀。例如,以下集合是前缀代码:Algorithm 生成';n';二进制前缀码,algorithm,language-agnostic,huffman-code,Algorithm,Language Agnostic,Huffman Code,A是一组代码,因此没有代码是另一个代码的前缀。例如,以下集合是前缀代码: 10 11 000 001 0100 0101 0110 0111 具有n=8个成员。我认为它们通常是用某种类型的哈夫曼树创建的 我的问题是:你能帮我创建一个函数,生成一个包含“n”个成员的二进制前缀代码吗 大概是这样的: 列出GenerateBinaryPrefixCodes(int n) 此外,要求是在比特的总和最小化的意义上是“最优的” 我更喜欢C/C++/C#/类似的答案。这不是真正的家庭作业,但我这样标记它,因
10
11
000
001
0100
0101
0110
0111
具有n=8个成员。我认为它们通常是用某种类型的哈夫曼树创建的
我的问题是:你能帮我创建一个函数,生成一个包含“n”个成员的二进制前缀代码吗
大概是这样的:
列出GenerateBinaryPrefixCodes(int n)代码>
此外,要求是在比特的总和最小化的意义上是“最优的”
我更喜欢C/C++/C#/类似的答案。这不是真正的家庭作业,但我这样标记它,因为它听起来像是一个很好的硬件问题
谢谢 比特数之和最小化的要求相当于要求每个符号出现一次的字符串的代码是最优哈夫曼码。因此,只需创建一个包含n个唯一字符的字符串,并为其生成一个哈夫曼树 请看一看。它将为您提供有用的C++结构。我在右边的“相关”部分看到了其他类似的问题,可能会有所帮助
我以前用递归算法在C中做过这件事,是的,这将是一个很好的家庭作业问题。生成问题(解码的唯一性)可以通过构建一个由n个叶节点组成的二叉树来保证,并枚举每个这样的节点在树中的位置(0是左分支,1是右分支)。你是对的,哈夫曼树有这个属性。请注意,对于哈夫曼树,每个节点的权重等于其代表字符的频率,并且该树使用递归属性构建,即节点连接的左右决策基于到该点的子节点的总和。这种累积和特性也是斐波那契分布对哈夫曼树进行最坏情况压缩的原因
注意,哈夫曼编码是固定字母的变量编码的最佳选择。非固定字母表的一个例子是决定将“the”作为要压缩的集合中的单个元素(而不是两个空格和每个字母一个)
您的问题似乎与替换无关。您只需要n个元素的前缀码,其中所有前缀码的长度之和都最小化。这与构建每个元素频率为1的哈夫曼树相同(因为它保证了总编码字符串的最小编码,对您来说,它等于每个编码元素的位的总和正好一次,即最小化总位)。注意:这保证了最小编码,但不能保证最快的实现。您可能不需要为每个方法调用构建树。不幸的是,我对实现一无所知。您的n=8示例似乎并不代表最佳解决方案
10 11 000 001 0100 0101 0110 0111
总比特数:26
000 001 010 011 100 101 110 111
总比特数:24
当频率恒定时,最佳前缀编码将为固定长度。每个前缀代码的长度为log(n),是字母表0..n-1的二进制表示形式
对于n不是2的幂的情况进行编辑
// generate tree
function PCode(n) {
var a = [];
for(var x=1; x<=n; x++) {
a.push({"v":x});
}
for(var x=0; x<n-1; x++) {
var node = {"v": null, "l": a.shift(), "r": a.shift()};
a.push(node);
}
return a.pop();
}
//print
function Print(node, s) {
if(node["v"] != null) {
console.log(s);
}
if(node["l"] != null) Print(node["l"], s + "0");
if(node["r"] != null) Print(node["r"], s + "1");
return;
}
//test
Print(PCode(3), "");
//生成树
函数PCode(n){
var a=[];
对于(var x=1;x让我们用二进制表示为1x的数字对二进制字符串x进行编码。否则,0和00将映射到相同的int
std::vector<int> GenerateBinaryPrefixCodes(int n) {
std::vector<int> list;
for (int i = n; i != 2 * n; ++i) list.push_back(i);
return list;
}
std::向量生成器INARYPREFIXCODE(int n){
std::向量表;
对于(int i=n;i!=2*n;++i)列表。向后推(i);
退货清单;
}
前缀代码
正如您所指出的,前缀代码是指给定代码不是任何其他给定代码的前缀的代码。
这是一个非常通用的定义。哈夫曼编码是前缀代码的一种受限形式
哈夫曼编码的一个常见用法是最小化(优化)编码“消息”所需的总比特数。
“消息”通常是一个符号序列,通过将每个符号的出现映射到
一个特定的前缀代码,并在其位置写出前缀代码。可以使用任何一组前缀代码
但是,哈夫曼编码将根据位计数产生尽可能短的消息
例如,可以将ASCII字符集视为符号到一组8位前缀代码的映射。
这甚至可以被认为是哈夫曼编码,只要编码的消息完全包含
每个可能符号的编号相同
当要编码的消息包含不相等的符号频率时,有趣的事情就开始了
第一点可以通过使用不同长度的前缀代码来减少消息的总比特长度
前缀代码用于频率较高的符号,较长的前缀代码用于频率较低的符号
根据您的示例,有8个符号需要编码。映射到前缀代码“11”和“10”的符号最多
消息中的频繁符号。同样,映射到“0111”、“0110”、“1010”和“0100”的符号的频率最低。
频率越高,前缀代码越短
创建哈夫曼编码的“诀窍”是构建前缀代码集,以便在映射之后
消息中的每个符号与其关联的前缀代码相关联。消息包含尽可能少的位
我发现将前缀代码视为二叉树很有用,其中每个叶节点映射到一个符号。
例如,与问题中给出的前缀代码对应的二叉树(01,11,000,001,
0100101010111)将是:
+-- (11)
+--+
| +-- (10)
|
| +-- (0111)
--+ +--+
| | +-- (0110)
| +--+
| | | +-- (0101)
| | +--+
+--+ +-- (0100)
|
| +-- (001)
+--+
+-- (000)
+-- (1 bit)
--+
| +-- (2 bits)
+--+
| +-- (3 bits)
+--+
+-- (3 bits)
要获取括号中的值,只需在紧跟顶部边缘时指定“1”,如果紧跟底部边缘,则指定“0”
边缘跟随
如何建造这样一棵树?
从表示二叉树和列表的数据结构开始
二叉树将包含两种类型的
+-- (1 bit)
--+
| +-- (2 bits)
+--+
| +-- (3 bits)
+--+
+-- (3 bits)
+-- (2 bits)
+--+
| +-- (2 bits)
--+
| +-- (2 bits)
+--+
+-- (2 bits)
+--+ (1)
|
--+
| +-- (01)
+--+
+-- (00)
+-- (0)
|
--+
| +-- (10)
+--+
+-- (11)
+-- (??)
+--+
| +-- (??)
--+
|
+-- (?)