繁体   English   中英

逆霍夫曼编码

[英]Reversed Huffman coding

假设我有一个带有预定义二进制前缀代码的单词集合。 给定非常大的随机二进制数据块,我可以使用前缀代码将此数据块解析为单词。

我想至少确定(对于很大长度的随机块)每个单词的命中次数的期望值(在解码文本中提到多少次)。

乍一看,这个问题显得微不足道-从随机位池中扫描每个单词的概率完全取决于其长度(因为每个位可以为0或1)。 但是我怀疑这是上述问题的不正确答案,因为单词的长度不同,因此该概率与预期的命中数(除以数据块的长度)不同。

UPD:有人要求我(在下面的评论中)用数学方式说明此问题,所以就到这里了。

w为仅用零和一书写的单词的列表(我们的字母仅包含两个字母)。 此外, w中的任何单词都不是任何其他单词的前缀。 因此, w形成合法的二进制前缀代码。 我想知道(至少近似地) w中每个单词的命中平均值,该平均值是在大小固定为n的所有可能二进制数据块上平均的。 n可以取的非常大,比我们所讲单词的任何长度都大得多。 但是,单词的长度不同,这是不能忽略的。

我会很感谢任何尝试解决此问题的参考。

我的简短回答:可以为每个给定的单词列表计算预期的点击次数(或预期的点击比例)。

我不会描述完整的算法,但只是做下面的例子中详细加以说明:让我们修复的三个字下面很简单的列表: 01011

对于每一个n ,有2^n长度的不同数据块n (我的意思n比特),每发生以相同的概率2^(-n)

第一个观察结果是,并非所有数据块都可以被完全解码-例如数据0101 ,当您解码时,最后将只保留一个1

让我们为可以精确解码的长度为n数据块的数量写U(n) ,为其他(即末尾有1 V(n)V(n) )。 以下重复关系很清楚:

  1. U(n) + V(n) = 2^n

  2. V(n) = U(n - 1)

初始值U(0) = 1且V(0) = 0

一个简单的计算将得出:

U(n) = (2^(n + 1) + (- 1)^n) / 3

现在,让我们A(n) (相应地, B(n)C(n)是命中数的字上的总和0 (分别1011 )对所有的U(n)确切的数据块,并且对于所有V(n)不精确的数据块,令a(n) (分别为b(n)c(n) )为相同的总和V(n)在这种情况下,最后1不计算在内)。

那么我们有以下关系:

  1. a(n) = A(n - 1)b(n) = B(n - 1)c(n) = C(n - 1)
  2. A(n) = A(n - 1) + U(n - 1) + A(n - 2) + A(n - 2)
  3. B(n) = B(n - 1) + B(n - 2) + U(n - 2) + B(n - 2)
  4. C(n) = C(n - 1) + C(n - 2) + C(n - 2) + U(n - 2)

关系说明2 3 4:

如果D是长度为n的精确数据块,则存在三种可能性:

  • D0结尾,删除该0得到一个精确的数据块,长度为n - 1

  • D10结尾,删除10得到一个精确的数据块,长度为n - 2

  • D11结尾,删除此11得到长度为n - 2的精确数据块。

因此,例如,当我们对长度为n所有精确数据块中所有0的命中数求和时,这三种情况的贡献分别为A(n - 1) + U(n - 1)A(n - 2)A(n - 2) 对于其他两个平等,情况类似。

现在,解决这些递归关系,我们得到:

  • A(n) = 2/9 * n * 2^n + (smaller terms)

  • B(n) = C(n) = 1/9 * n * 2^n + (smaller terms)

因为U(n) = 2/3 * 2^n + (smaller terms) ,我们的结论是,有大约n/3命中上0n/6命中上10n/6命中上11

请注意,由于我们还考虑了V(n)不精确的数据块,因此比例相同,这是因为A(n)B(n)C(n)U(n)a(n)b(n)c(n)V(n)

此方法可推广到任何单词列表。 这与使用动态编程解决此问题的想法相同-创建状态,找到递归关系并建立转换矩阵。

更进一步

我认为以下情况也可能成立,这将进一步简化答案。

w_1 ,..., w_k为列表中的单词,令l_1 ,..., l_k为它们的长度。

对于每个i = 1, ..., k ,令a_iw_i命中的比例,即对于长度为n数据块, w_i的预期命中数为a_i * n + (smaller terms)

然后,我的感觉(猜想)是a_i * 2^(l_i)对于所有i都是相同的,即,如果一个单词比另一个单词长一点,那么它的命中数是另一个的一半。

如果正确,这个猜想可能很难证明。 但是我懒得现在想...

如果这是真的,那么我们可以很容易地计算出这些a_i ,因为我们具有以下身份:

sum (a_i * l_i) = 1

让我用上面的例子来说明这一点。

我们有w_1 = 0w_2 = 10w_2 = 10 w_3 = 11 ,因此l_1 = 1l_2 = l_3 = 2

根据猜想,我们应该有a_1 = 2 * a_2 = 2 * a_3 因此a_2 = a_3 = xa_1 = 2x 上面的等式变为:

2x * 1 + x * 2 + x * 2 = 1

因此x = 1 / 6 ,我们有a_1 = 1 / 3a_2 = a_3 = 1 / 6 ,可以通过上述计算验证。

让我们做一个可以识别单词的简单机器:一个DFA,每个单词都有接受状态。 要构建此DFA,请从一棵二叉树开始,其中每个左子边缘标记为0,每个右子边缘标记为1。每个叶子都是一个单词接受器(如果到树的那条叶子的路径是单词的拼写)还是垃圾邮件(不是有效单词前缀的字母字符串)。 我们将叶子的“重新启动”边缘连接回树的根*。

如果我们有一个无限长的字符串,让我们找出匹配每个单词的频率。 为此,请将DFA的图视为Markov状态转移图,将起始状态初始化为概率为1且所有其他状态为0的根,然后找到稳态分布(通过找到过渡的主要特征向量)图的相应矩阵)。

我们的字符串长度不是无限的。 但是由于n很大,所以我预计“边缘效应”不会有太大的影响。 我们可以通过逐字匹配乘以n来近似逐字匹配频率 如果我们想更精确,则不用取特征向量,而只需将过渡矩阵取到第n次幂,然后将其乘以起始分布即可得到n个字母后的结果分布。

*这不是很精确,因为此Markov系统会在根上花费一些非零的时间,当识别一个单词或跳过垃圾后,它应立即取决于0子或1子。 因此,我们实际上并没有将“重新启动”边连接到根:从一个接受单词的节点,我们连接了两个重新启动边(一个连接到根的0子节点,一个连接到根的1子节点); 我们将左子节点的垃圾节点替换为0子节点的边缘; 我们用1个孩子的边缘替换了右孩子的垃圾节点。 实际上,如果我们以0.5的概率将初始状态设置为0,以0.5的概率将初始状态设置为1,则甚至不需要根。

编辑:要使用@WhatsUp的示例,我们首先使用如下所示的DFA:

DFA for WhatsUp示例词集

我们重新布线一下,以在接受单词后重新启动,并摆脱根节点:

在此处输入图片说明

相应的马尔可夫转移矩阵为:

0.5    0  0.5  0.5
0.5    0  0.5  0.5
  0  0.5    0    0
  0  0.5    0    0

其第一个特征向量为:

0.333
0.333
0.167
0.167

也就是说,它在0节点上花费了其时间的1/3,在1中花费了1/3,在10中花费了1/6,在11中花费了1/6。这与@WhatsUp的示例结果一致。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM