我该如何计算比特串的近似熵?

57

有没有标准的方法来做这个?

通过谷歌搜索 -- "approximate entropy" bits -- 可以找到多篇学术论文,但我只想找到一段伪代码,用于定义任意长度的位串的近似熵。

(如果这比说起来容易得多,并且取决于应用程序,我的应用程序涉及16,320个加密数据 (密文)。虽然是作为谜题加密的,但并不意味着不可能破解。我想先检查熵,但很难找到一个好的定义。所以这似乎是一个应该在StackOverflow上提问的问题!如果有关解密这16k看起来随机的位的想法,也欢迎分享...)

另请参阅相关问题:
计算机科学中熵的定义是什么?

7个回答

40

熵并不是你所得到的字符串本身的属性,而是你本可以获得的字符串的性质。换句话说,它描述了生成该字符串的过程

在简单情况下,你从一个包含N个可能字符串的集合中获得一个字符串,其中每个字符串被选中的概率相同,即1/N。在这种情况下,该字符串的熵为N。熵通常用位表示,它是一个对数尺度: “n位”熵是等于2n的熵。

例如:我喜欢将我的密码生成为两个小写字母、两个数字、两个小写字母和最后两个数字 (例如 va85mw24)。字母和数字是随机、均匀且彼此独立地选择的。此过程可产生4569760000个不同的密码,并且所有这些密码被选择的机会相等。这样一个密码的熵就是4569760000,大约是32.1位。


这是正确的,但我可能没有正确地提出问题。请看我给出的答案,也许可以表明我本来想问的问题。但我认为将一个比特串的“近似熵”称为标准可能是合适的。无论如何,这个答案是有用和相关的;谢谢! - dreeves
4
答案中提到了字符限制,因此可用的字母表并不是对于密码中每个字符都有36个字符。对于由36个字符组成、长度不受限制的密码,您的计算是正确的;但是在答案中所解释的情况下,这些额外的限制使它更加有趣,并且更具说明性。 - tripleee
4
限制条件是前两个字符必须为小写字母(字母表共有26个字符),接下来的两个字符必须为数字(数字表共有10个字符),以此类推。我认为这已经非常清晰明了,无法再更加清晰了。 - tripleee
@tripleee 错了,你应该再读一遍答案。这里有个提示:字母和数字是随机、均匀且相互独立地选择的。-- 因此任何组合都是有效的,甚至像“00000000”之类的字符串也是如此。 - specializt
4
令人嘘声连连。在这些限制条件下。 00000000 违反了约束条件。在前两个字母的组中,它们是随机、均匀和独立选择的。然后从数字池中随机、均匀和独立地取出两个数字。 - tripleee
显示剩余2条评论

37

香农熵方程是标准的计算方法。以下是一个简单的Python实现,它毫不掩饰地从Revelation代码库中复制而来,并因此获得GPL许可:

import math


def entropy(string):
    "Calculates the Shannon entropy of a string"

    # get probability of chars in string
    prob = [ float(string.count(c)) / len(string) for c in dict.fromkeys(list(string)) ]

    # calculate the entropy
    entropy = - sum([ p * math.log(p) / math.log(2.0) for p in prob ])

    return entropy


def entropy_ideal(length):
    "Calculates the ideal Shannon entropy of a string with given length"

    prob = 1.0 / length

    return -1.0 * length * prob * math.log(prob) / math.log(2.0)

请注意,此实现假定您的输入比特流最好表示为字节。这可能与您的问题域有关,也可能不相关。您真正想要的是将比特流转换为数字字符串。如何决定这些数字取值是特定于领域的。如果您的数字只是1和0,则将比特流转换为一个由1和0组成的数组。但您选择的转换方法将影响您得到的结果。


1
根据Cypherpunks的回答,这假设模型中每个位置每个字符出现的可能性相等。 - President James K. Polk
2
@fmark @dreeves 信息熵取决于可用状态的数量。由于二进制字符串只有2种可能的状态,因此最大熵始终为1。 - Chris de Vries
@cdv:是的,但每个字符有2种状态!你声称一个随机的512位字符串具有与单个位“0”/“1”相同的熵,这是错误的。熵描述了我们对系统的不了解或猜测正确比特模式的难度,并且随着字符串长度的增加而添加(因为它是“log(可能值的数量)”)。要纠正,请将返回值乘以“string”的长度。 - Christian Aichinger
这是不正确的。它需要考虑正在使用的字符总数。例如,"01010101"的熵为1.0,最大/理想熵为1.0,因为它是二进制的。为了达到最大熵,这个公式总是需要与插槽数量一样多的字符。很容易,它可以接受一个参数,用于给定字符串的可用字符总数。 - Xodarap777
为了验证我的假设,我尝试在π的前1000个数字上运行它,这些数字具有统计上均匀和随机分布的0-9数字。熵成功计算为约3.3,但entropy_ideal假定有1000个可能的字符。我使用的解决方案是使用entropy_ideal(string)并将length的两个实例替换为len(set(string)) - 假设使用了所有可能的字符。它也可以很容易地成为手动输入。 - Xodarap777
显示剩余6条评论

21

我认为答案是字符串的科尔莫戈洛夫复杂度。 虽然这不是一个伪代码块可以回答的,但科尔莫戈洛夫复杂度不是一个可计算函数

实际上你能做的一件事情就是用最好的数据压缩算法压缩二进制字符串。 它被压缩得越多,熵就越低。


2
一个小修正,低压缩表示低熵,因为低熵等于低无序性。[熵、压缩和信息内容] (http://www.isi.edu/~vfossum/entropy.pdf) - lsalamon
基于这些直觉,香农开发了一种针对语言的熵度量,将无序随机的第一句话赋予高熵值,将有序、模式化的第二句话赋予低熵值...引用自您提供的 @isalamon 论文。 - VP.
@lsalamon,链接已损坏。 - Valmiky Arquissandas
@ValmikyArquissandas,这里有一篇关于的论文。 - lsalamon
3
高压缩率 => 低熵。低压缩率 => 高熵。 - mechanicious

10
NIST随机数生成器评估工具包有一种计算“近似熵”的方法。以下是简要描述: “近似熵测试描述:此测试的重点是每个和所有重叠的m位模式的频率。测试的目的是将相邻长度(m和m+1)的重叠块的频率与随机序列的预期结果进行比较。” 更详细的解释可以从此页面上PDF获取。

http://csrc.nist.gov/groups/ST/toolkit/rng/documentation_software.html


2
这可能有点晚了,但我在github上找到了一个相当新的代码片段,用于NIST实现ApEn:https://gist.github.com/StuartGordonReid/ff86c5a895fa90b0880e - Chaoste

8
没有一个单一的答案。熵总是相对于某个模型的。当有人谈论密码的熵受限时,他们的意思是“相对于智能攻击者的预测能力”,并且它总是一个上限。
你的问题是,你试图测量熵以帮助你找到一个模型,这是不可能的;熵测量可以告诉你一个模型有多好。
话虽如此,有一些相当通用的模型可以尝试;它们被称为压缩算法。如果gzip可以很好地压缩您的数据,则至少找到了一个可以很好地预测它的模型。例如,gzip大多数情况下不敏感于简单的替换。它可以像处理“the”一样轻松处理文本中频繁出现的“wkh”。

2
我不确定我理解你的第二段。 - dreeves

3
使用此公式计算单词的香农熵:http://imgur.com/a/DpcIH 以下是一个O(n)的算法来计算它:
import math
from collections import Counter


def entropy(s):
    l = float(len(s))
    return -sum(map(lambda a: (a/l)*math.log2(a/l), Counter(s).values()))

1
这是一个Python的实现(我也将其添加到维基页面中):
import numpy as np

def ApEn(U, m, r):

    def _maxdist(x_i, x_j):
        return max([abs(ua - va) for ua, va in zip(x_i, x_j)])

    def _phi(m):
        x = [[U[j] for j in range(i, i + m - 1 + 1)] for i in range(N - m + 1)]
        C = [len([1 for x_j in x if _maxdist(x_i, x_j) <= r]) / (N - m + 1.0) for x_i in x]
        return -(N - m + 1.0)**(-1) * sum(np.log(C))

    N = len(U)

    return _phi(m) - _phi(m + 1)

例子:

>>> U = np.array([85, 80, 89] * 17)
>>> ApEn(U, 2, 3)
-1.0996541105257052e-05

上述示例与维基百科给出的示例一致。

2
m和r是什么? - Shannon
我还有另一个问题。使用这个函数,当randU = np.random.choice([0, 1], size=17 * 3), m = 2, r = 3时,我会得到0。这正常吗? - Shannon
@Shabnam,老实说我记不清了,我已经有一段时间没用过这个了。但是当我写它的时候,我对我的实现进行了相当彻底的测试。如果你查看维基百科文章,我相信你可以理解的。 - Ulf Aslak

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接