如何计算文件的熵?


74

如何计算文件的熵?(或者我们只说一堆字节)
我有一个主意,但是我不确定它在数学上是正确的。

我的想法如下:

  • 创建一个由256个整数组成的数组(全为零)。
  • 遍历文件及其每个字节,
    增加数组中的相应位置。
  • 最后:计算数组的“平均值”值。
  • 用零初始化一个计数器,
    并为每个数组条目:
    将条目的差值加到“平均值”的计数器上。

好吧,现在我被卡住了。如何以所有结果都在0.0到1.0之间的方式“投影”计数器结果?但是我敢肯定,这个主意还是有矛盾的...

我希望有人有更好,更简单的解决方案?

注意:我需要整个事情来对文件的内容进行假设:(
纯文本,标记,压缩或某些二进制文件,...)



1
您的意思是度量熵?熵除以消息长度
user2622016 2013年

哎呀,您添加的注释:Note: I need the whole thing to make assumptions on the file's contents: (plaintext, markup, compressed or some binary, ...)...您只是在请求神似的魔术,祝您好运,并开发出可证明的最佳数据压缩技术。
MickLH '16

您可以发布最终结果的伪代码吗?
Guy Kahlon

Answers:


51
  • 最后:计算数组的“平均值”值。
  • 用零初始化一个计数器,并为每个数组条目:将条目的差值加到“平均值”的计数器上。

一些修改,您可以得到香农的熵:

重命名“平均”为“熵”

(float) entropy = 0
for i in the array[256]:Counts do 
  (float)p = Counts[i] / filesize
  if (p > 0) entropy = entropy - p*lg(p) // lgN is the logarithm with base 2

编辑: 正如韦斯利所说,我们必须将熵除以8才能在0范围内进行调整1(或者,我们可以使用对数底数256)。


2
一个修正:你需要跳过与计数[I] == 0的元素
伊戈尔Krivokon

您说得对,Krivokon,谢谢!我看到卫斯理正确地做到了,除了他选择了一个“怪异”的对数底。
Nick Dandoulakis 09年

3
是的,这绝对很奇怪。但是,由于您使用的是更传统的对数基数2,因此您会得到一个介于0和8之间的值。您可能要提一下这一点,以便询问者可以记住将结果除以8即可得到一个介于0和1之间的值。 (尽管恭喜快速回答-我必须在Wikipedia上查找这些内容才能记住它::P)
Wesley

这是一个很好的方法,我使用它通过比较像素数据来分析图像的“熵”,并给出了良好的结果。
马特·沃伦

4
熵的这种估计假设字节是独立的,这通常是错误的。例如,拍摄从白色到黑色具有均匀水平梯度的灰度图像。
leonbloy

34

一个更简单的解决方案:gzip文件。使用文件大小的比率:(压缩大小)/(原始大小)作为随机性(即熵)的度量。

这种方法不能为您提供确切的绝对熵值(因为gzip不是理想的压缩器),但是如果您需要比较不同来源的熵,它就足够了。


1
我也有这个想法(作为最后的选择),但是我需要分析很多文件,因此gzip压缩所有文件不是有效的选择。
ivan_ivanovich_ivanoff 2009年

3
这取决于您的全部数量。我只是尝试将/ usr / bin中的所有文件gzip压缩,大约有1000个文件,即200Mb。花了大约7秒钟。这是您一次可以用来获取尺寸的命令:cat * | gzip --fast | wc -c。它比逐字节读取文件要慢,但速度并不慢。
Igor Krivokon 09年

gzip花费了很多年的精力进行大量的优化编程。也可以利用它。
Nosredna

3
实际上,这可以比接受的答案更好地估计熵-特别是在文件较大的情况下。
leonbloy

2
我同意这比接受的答案更好。实际上,有几篇学术论文都使用这种近似方法。
雨果·塞雷诺·费雷拉

33

要计算字节集合的信息熵,您需要执行与tydok的答案类似的操作。(tydok的答案适用于位集合。)

假定以下变量已存在:

  • byte_counts是文件中每个值的字节数的256个元素的列表。例如,byte_counts[2]是具有值的字节数2

  • total 是文件中的字节总数。

我将使用Python编写以下代码,但显而易见。

import math

entropy = 0

for count in byte_counts:
    # If no bytes of this value were seen in the value, it doesn't affect
    # the entropy of the file.
    if count == 0:
        continue
    # p is the probability of seeing this byte in the file, as a floating-
    # point number
    p = 1.0 * count / total
    entropy -= p * math.log(p, 256)

有几件事要注意。

  • 检查count == 0不仅是优化。如果为count == 0,则p == 0和log(p)将不确定(“负无穷大”),从而导致错误。

  • 256在调用math.log表示是可能的离散值的数目。由八位组成的字节将具有256个可能的值。

结果值将介于0(文件中的每个单个字节都相同)到1(每个字节的每个可能值之间均分)之间。


关于使用日志库256的说明

确实,该算法通常使用对数为2的对数。这给出了以位为单位的结果答案。在这种情况下,任何给定文件的最大熵数为8位。自己尝试:通过byte_counts列出所有12或的列表来最大化输入的熵100。当文件的字节均匀分布时,您会发现存在8位的熵。

可以使用其他对数底数。使用b = 2允许在比特的结果,因为每个位可以有2个值。使用b = 10会将结果放入dits或十进制位,因为每个dit有10个可能的值。使用b = 256将以字节为单位给出结果,因为每个字节可以具有256个离散值之一。

有趣的是,使用对数标识,您可以算出如何在单位之间转换所得的熵。除以8可以将以位为单位获得的任何结果转换为字节单位。作为一个有趣的,有意的副作用,这使熵的值介于0和1之间。

综上所述:

  • 您可以使用各种单位来表达熵
  • 大多数人用位表示熵(b = 2)
    • 对于字节集合,这给出了最大8位熵
    • 由于提问者希望结果在0到1之间,因此请将该结果除以8以得到有意义的值
  • 上面的算法以字节为单位计算熵(b = 256)
    • 等效于(比特熵)/ 8
    • 这已经给出了0到1之间的值

感谢您的评论...哦,去哪儿了?无论如何,我同意使用“字节频率”有点令人困惑。该术语已被删除。
韦斯利

现在+1。我同意您的意见和修改,尤其是重要的澄清,即这种方法以字节为单位提供熵,而通常的值以位为单位,尽管字节确实符合OP的要求。(很抱歉,之前的删除。我决定不想参与此活动,并希望在有人看到之前删除我的评论。)
tom10 2009年

这不是熵,它假设字节是独立的。请参阅我对尼克答案的评论
leonbloy 2013年

20

对于它的价值,这是用C#表示的传统(熵位)计算:

/// <summary>
/// returns bits of entropy represented in a given string, per 
/// http://en.wikipedia.org/wiki/Entropy_(information_theory) 
/// </summary>
public static double ShannonEntropy(string s)
{
    var map = new Dictionary<char, int>();
    foreach (char c in s)
    {
        if (!map.ContainsKey(c))
            map.Add(c, 1);
        else
            map[c] += 1;
    }

    double result = 0.0;
    int len = s.Length;
    foreach (var item in map)
    {
        var frequency = (double)item.Value / len;
        result -= frequency * (Math.Log(frequency) / Math.Log(2));
    }

    return result;
}

这是一个了不起的答案。为了扩展原始问题,如果答案是相对的而不是绝对的,您将如何计算?例如,假设您正在寻找地理熵。一个广告系列在全国范围内投放,您可以捕获受访者的地理位置。没有两个条目可能具有相同的坐标,但是某些熵函数仍应该能够告诉您可能存在一些局部热点,或者笼罩的全国分布将更加有效。
保罗·史密斯

1
不应该检查中的空值map吗?否则,Math.Log(frequency)可能会返回-INF
executifs 2014年

(Math.Log(frequency)/ Math.Log(2))== Math.Log(frequency,2)
citykid

16

ent可以处理吗?(或者它可能在您的平台上不可用。)

$ dd if=/dev/urandom of=file bs=1024 count=10
$ ent file
Entropy = 7.983185 bits per byte.
...

作为反例,这是没有熵的文件。

$ dd if=/dev/zero of=file bs=1024 count=10
$ ent file
Entropy = 0.000000 bits per byte.
...

1
谢谢!很高兴知道这个工具。但是我需要以编程方式并且以独立于平台的方式解决此问题,因此是我的问题。
ivan_ivanovich_ivanoff 2009年

1
+1感谢您的指导。至少在Debian中存在:packages.debian.org/wheezy/ent
Tripleee,2013年

14

我的回答迟到了两年,所以尽管投票数不多,但请考虑一下。

简短答案:使用下面的第一和第三大胆公式,可​​以了解大多数人以比特为单位对文件“熵”的看法。如果您想让香农的H熵实际上是熵/符号,正如他在论文中13次指出的那样(大多数人都不知道),则仅使用第一个方程。一些在线熵计算器使用这一函数,但是Shannon的H是“特定熵”,而不是“总熵”,它引起了很多混乱。如果希望答案在0和1之间,则使用1st和2nd等式,这是归一化的熵/符号(不是位/符号,而是通过让数据选择其自己的对数基数来对数据的“熵性质”进行真正的统计度量而不是任意分配2,e或10)。

N个符号的文件(数据)有4种类型的熵,其中n种具有唯一的符号类型。但是请记住,通过了解文件的内容,您可以知道文件所处的状态,因此S = 0。准确地说,如果您拥有一个可以访问大量数据的数据源,则可以计算该数据源的预期未来熵/字符。如果在文件上使用以下内容,则更准确地说是从该来源估计其他文件的预期熵。

  • 香农(特定)熵H = -1 * sum(count_i / N * log(count_i / N))
    ,其中count_i是符号i在N中出现的次数。
    如果log是2,nats / symbol,单位是位/符号。如果自然日志。
  • 归一化比熵:H / log(n)
    单位是熵/符号。范围从0到1。1表示每个符号均等次出现,而接近0则是除1以外的所有符号都只出现一次,而很长文件的其余部分则是另一个符号。日志与H的底数相同。
  • 绝对熵 S = N * H
    如果log是2,单位是位,如果ln()),单位是位。
  • 归一化绝对熵S = N * H / log(n)
    单位是“熵”,范围从0到N。对数与H具有相同的底数。

尽管最后一个是最真实的“熵”,但第一个(香农熵H)却是所有书都称为“熵”的,而没有(需要IMHO)资格。大多数人并没有像香农一样澄清它是每个符号的比特/符号还是熵。称H为“熵”的话太松散了。

对于每个符号频率相等的文件:S = N * H =N。对于大多数大的位文件,情况就是如此。熵不对数据进行任何压缩,因此完全不了解任何模式,因此000000111111的H和S与010111101000相同(两种情况下均为6 1和6 0)。

就像其他人所说的那样,使用诸如gzip之类的标准压缩例程并在前后进行分割将更好地度量文件中预先存在的“顺序”量,但是这会偏向于更适合压缩方案的数据。没有可用于定义绝对“顺序”的通用完美优化压缩机。

要考虑的另一件事:如果更改数据表示方式,则H会更改。如果您选择不同的位组合(位,半字节,字节或十六进制),则H将不同。因此,除以log(n),其中n是数据中唯一符号的数量(二进制为2,字节为256),H的范围为0到1(这是标准化的密集香农熵,以每个符号的熵为单位) 。但是从技术上讲,如果256种字节中只有100种出现,则n = 100,而不是256。

H是“强”熵,即它是每个符号,类似于物理学中的特定熵,即每千克或每摩尔的熵。类似于物理学S的文件的规则“广义”熵为S = N * H,其中N是文件中的符号数。H将完全类似于理想气体体积的一部分。从更深的意义上讲,信息熵不能简单地完全等于物理熵,因为物理熵允许“有序的”以及无序的排列:物理熵比完全随机的熵(例如压缩文件)更多。不同的一个方面对于理想的气体,有一个额外的5/2因子可以解决这个问题:S = k * N *(H + 5/2)其中H =每个分子可能的量子态=(xp)^ 3 / hbar * 2 * sigma ^ 2,其中x =盒子的宽度,p =系统中总的非方向动量(根据动能和每个分子的质量计算),sigma = 0.341,符合不确定性原理,仅给出1 std开发内的可能状态。

进行一些数学运算即可得出文件的归一化广义熵的较短形式:

S = N * H / log(n)=总和(count_i * log(N / count_i))/ log(n)

这个单位是“熵”(实际上不是单位)。它被归一化为比N * H的“熵”单位更好的通用度量。但是,在没有澄清的情况下也不应称为“熵”,因为正常的历史惯例是错误地将H称为“熵”(这与Shannon案文中的澄清)。


我想对您的答案表示赞同,但是您首先需要澄清一些歧义:在等式2和4中,您说“因此,您除以log(n),其中n是数据中唯一符号的数量”,记录n的什么?自然对数,log2(n)?通常在数学中,未指定底数,log(n)表示log10(n)。请说清楚。
亚当·怀特

我在等式1和3中提到,用户选择基准。对于方程式2和4,它应该是相同的底数(H在其中)。我将添加说明。
zawy

10

没有文件的熵。在信息论中,熵是随机变量的函数,而不是固定数据集的函数(嗯,从技术上讲,固定数据集确实具有熵,但是熵为0,我们可以将数据视为随机分布,只有一种可能的结果,概率为1)。

为了计算熵,您需要一个随机变量来对文件进行建模。然后,熵将是该随机变量的分布的熵。该熵将等于该随机变量中包含的信息位数。


4
我不了解熵的理论定义。但是,每个术语总是有两种语义:理论术语和流行术语。好吧,似乎这里的每个人都理解了流行的部分;)
ivan_ivanovich_ivanoff 2009年

1
关于某人如何将“文件的熵”转换为严格的数学定义的答案,至少有两种明显的解释。如果您真的想了解自己在做什么,则应该了解在这些答案中对熵建模的统计方式。
James Thompson

1
或者您可以进入Kolmogorov复杂度,这是一个更好的数学定义,但却无可辩驳。
杰弗里·汉汀

@JamesThompson有趣,是否有任何指针可以指示您要从要测量其熵的一堆文件中推断出此随机变量?
Vladtn 2012年

4
我相信在此问题中,随机变量是通过遍历文件而在文件中找到的字节。因此,它将是具有256个可能值的离散随机变量,并且其自身分布取决于文件。(我知道这篇文章很旧,但这可能会澄清来这里的人的意思)
Anoyz 2012年

5

如果您使用信息论熵,请注意不要在字节上使用它。说,如果您的数据由浮点数组成,则应改编这些浮点数的概率分布并计算该分布的熵。

或者,如果文件的内容是Unicode字符,则应使用这些字符,等等。


我想当我想对任何类型的文件进行数据分析时,字节是我的最佳选择(作为一种折衷方法)。
ivan_ivanovich_ivanoff 2009年

1
当然可以。但是,您应该使用可以获得的所有其他信息。否则,您的结果可能会非常差。
拜耳

通常,无用是绝对正确的。香农熵不会为您提供有关文件内容的足够信息。每个压缩器都有两个阶段:建模和熵编码。熵编码是必需的,但是大多数冗余是在建模阶段检测到的(除非您要处理准随机数据)。
Igor Krivokon 09年

通常没用的就在这里。解决这个问题的一种方法是用语言说出您正在计算的全部内容:“我用来表示浮点数的ascii符号的熵是多少”,这是可以计算的,但是可能不是您想要的。
tom10

1
这是评论,而不是答案。
JasonMArcher 2014年

2

计算大小为“ length”的任何无符号字符的字符串的熵。这基本上是对http://rosettacode.org/wiki/Entropy上找到的代码的重构。我将其用于64位IV生成器,该生成器创建了100000000 IV的容器,没有重复,平均熵为3.9。http://www.quantifiedtechnologies.com/Programming.html

#include <string>
#include <map>
#include <algorithm>
#include <cmath>
typedef unsigned char uint8;

double Calculate(uint8 * input, int  length)
  {
  std::map<char, int> frequencies;
  for (int i = 0; i < length; ++i)
    frequencies[input[i]] ++;

  double infocontent = 0;
  for (std::pair<char, int> p : frequencies)
  {
    double freq = static_cast<double>(p.second) / length;
    infocontent += freq * log2(freq);
  }
  infocontent *= -1;
  return infocontent;
 }

2

回复:我需要整个事情来对文件的内容进行假设:(纯文本,标记,压缩或某些二进制文件,...)

正如其他人指出的(或被其迷惑/分心),我认为您实际上是在谈论度量熵(熵除以消息长度)。在Entropy(信息论)-Wikipedia上查看更多内容

链接到扫描数据以获取熵异常的抖动注释与您的基本目标非常相关。最终链接到libdisorder(用于测量字节熵的C库)。这种方法似乎可以为您提供更多信息,因为它可以显示度量熵在文件的不同部分中如何变化。参见此图,了解来自4 MB jpg图像(y轴)的256个连续字节的块的熵对于不同的偏移量(x轴)如何变化。在开始和结束时,熵是较低的,因为它是半途而废的,但是对于大多数文件来说,它约为每字节7位。

在此处输入图片说明 资料来源:https : //github.com/cyphunk/entropy_examples。[请注意,此图和其他图可通过新颖的http://nonwhiteheterosexualmalelicense.org许可获得。... ]

分析FAT格式的磁盘的字节熵|中的分析和类似图形| GL.IB.LY

整个文件和/或文件的第一个和最后一个块的统计信息(如度量熵的最大值,最小值,众数和标准偏差)可能对签名很有帮助。

这本书似乎也很相关:用于电子邮件和数据安全的文件伪装的检测和识别-Springer


-2

没有任何附加信息,文件的熵(根据定义)等于其大小* 8位。假定以下条件,文本文件的熵大小约为* 6.6位:

  • 每个字符都可能
  • 字节中有95个可打印字符
  • log(95)/ log(2)= 6.6

在英文文本文件中的熵估计是每个字符约0.6至1.3位(如解释在这里)。

通常,您不能谈论给定文件的熵。熵是一组文件的属性。

如果需要熵(准确地说是每字节熵),最好的方法是使用gzip,bz2,rar或任何其他强压缩方式对其进行压缩,然后将压缩后的大小除以未压缩的大小。这将是对熵的一个很好的估计。

正如尼克·丹杜拉基斯(Nick Dandoulakis)所建议的那样,逐字节地计算熵给出了一个很差的估计,因为它假设每个字节都是独立的。例如,在文本文件中,字母后面的空格比空格或字母标点符号小得多,因为单词通常长于2个字符。因此,下一个字符在z范围内的概率与前一个字符的值相关。不要对任何真实数据使用Nick的粗略估计,而应使用gzip压缩率。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.