78

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

我的想法如下:

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

好吧,现在我被困住了。如何“投影”计数器结果以使所有结果都介于 0.0 和 1.0 之间?但我敢肯定,无论如何,这个想法是不一致的......

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

注意:我需要对文件的内容做出假设:(
纯文本、标记、压缩或一些二进制文件,...)

4

12 回答 12

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)。

于 2009-06-13T11:05:41.047 回答
34

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

这种方法不会给你熵的确切绝对值(因为 gzip 不是一个“理想”的压缩器),但如果你需要比较不同来源的熵,它就足够了。

于 2009-06-13T10:38:46.913 回答
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列出所有1or2或来最大化输入的熵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 之间的值
于 2009-06-13T13:14:32.720 回答
22

对于它的价值,这是用 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;
}
于 2011-02-21T22:01:49.183 回答
17

这是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.
...
于 2009-06-13T20:58:50.717 回答
16

我迟到了两年的回答,所以尽管只有少数赞成票,但请考虑一下。

简短的回答:使用下面我的第一个和第三个粗体等式来了解大多数人在说文件的“熵”时的想法。如果你想要香农的 H 熵,它实际上是熵/符号,只使用第一个方程,正如他在他的论文中所说的 13 次大多数人都不知道的那样。一些在线熵计算器使用这个,但香农的 H 是“特定熵”,而不是“总熵”,这引起了很多混乱。如果您想要 0 和 1 之间的归一化熵/符号的答案,请使用第 1 和第 2 等式(它不是位/符号,而是通过让数据选择自己的对数基数来对数据的“熵性质”进行真正的统计测量)而不是任意分配 2、e 或 10)。

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

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

尽管最后一个是最真实的“熵”,但第一个(香农熵 H)是所有书籍所称的没有(恕我直言)限定的“熵”。大多数人没有澄清(像香农所做的那样)它是位/符号或每个符号的熵。将 H 称为“熵”太松散了。

对于每个符号的频率相等的文件:S = N * H = N。这是大多数大文件的情况。熵不对数据进行任何压缩,因此完全不知道任何模式,因此 000000111111 与 010111101000 具有相同的 H 和 S(两种情况下都有 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 个标准差内的可能状态。

一个小数学给出了文件的归一化扩展熵的更短形式:

S=N * H / log(n) = sum(count_i*log(N/count_i))/log(n)

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

于 2016-01-21T12:43:43.447 回答
10

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

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

于 2009-06-13T20:47:43.707 回答
5

如果您使用信息论熵,请注意不要在字节上使用它可能是有意义的。比如说,如果您的数据由浮点数组成,您应该改为将概率分布拟合到这些浮点数并计算该分布的熵。

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

于 2009-06-13T11:33:43.710 回答
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;
 }
于 2014-12-10T21:50:12.827 回答
2

回复:我需要对文件的内容做出假设:(纯文本、标记、压缩或一些二进制文件,...)

正如其他人所指出的(或被混淆/分心),我认为您实际上是在谈论度量熵(熵除以消息长度)。在熵(信息论)-维基百科上查看更多信息。

jitter 链接到Scanning data for entropy anomalies的评论与您的​​基本目标非常相关。这最终链接到libdisorder(用于测量字节熵的 C 库)。这种方法似乎可以为您提供更多信息,因为它显示了度量熵在文件的不同部分是如何变化的。例如,请参见这张图,了解来自 4 MB jpg 图像(y 轴)的 256 个连续字节块的熵如何随不同偏移量(x 轴)变化。在开始和结束时,熵较低,因为它是一部分,但对于大多数文件来说,它约为每字节 7 位。

在此处输入图像描述 来源:https ://github.com/cyphunk/entropy_examples 。[请注意,此图表和其他图表可通过小说http://nonwhiteheterosexualmalelicense.org许可证获得.... ]

更有趣的是 Analyzing the byte entropy of a FAT formatted disk |中的分析和类似图表。GL.IB.LY

整个文件和/或其第一个和最后一个块的度量熵的最大值、最小值、模式和标准偏差等统计数据作为签名可能非常有用。

这本书似乎也很相关:Detecting and Recognition of File Masquerading for E-mail and Data Security - Springer

于 2016-02-11T20:05:19.817 回答
0

这是一个基于此片段的 Java 算法以及无限战争期间发生的入侵

 public static double shannon_entropy(File file) throws IOException {
        byte[] bytes= Files.readAllBytes(file.toPath());//byte sequence
        int max_byte = 255;//max byte value
        int no_bytes = bytes.length;//file length
        int[] freq = new int[256];//byte frequencies
        for (int j = 0; j < no_bytes; j++) {
            int value = bytes[j] & 0xFF;//integer value of byte
            freq[value]++;
        }
        double entropy = 0.0;
        for (int i = 0; i <= max_byte; i++) {
            double p = 1.0 * freq[i] / no_bytes;
            if (freq[i] > 0)
                entropy -= p * Math.log(p) / Math.log(2);
        }
       return entropy;
    }

usage-example:

 File file=new File("C:\\Users\\Somewhere\\In\\The\\Omniverse\\Thanos Invasion.Log");
 int file_length=(int)file.length();
 double shannon_entropy=shannon_entropy(file);
 System.out.println("file length: "+file_length+" bytes");
 System.out.println("shannon entropy: "+shannon_entropy+" nats i.e. a minimum of "+shannon_entropy+" bits can be used to encode each byte transfer" +
                    "\nfrom the file so that in total we transfer atleast "+(file_length*shannon_entropy)+" bits ("+((file_length*shannon_entropy)/8D)+
                    " bytes instead of "+file_length+" bytes).");

output-example:

文件长度:5412 字节

香农熵:4.537883805240875 nats 即最少 4.537883805240875 位可用于对从文件传输的每个字节进行编码,因此我们总共传输至少 24559.027153963616 位(3069.878394245452 字节而不是 541 字节)。

于 2021-06-18T08:35:30.170 回答
-2

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

  • 每个字符的概率相同
  • 字节中有 95 个可打印字符
  • 对数(95)/对数(2) = 6.6

英文文本文件的熵估计约为每个字符 0.6 到 1.3 位(如此所述)。

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

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

正如 Nick Dandoulakis 所建议的那样逐字节计算熵给出了一个非常糟糕的估计,因为它假设每个字节都是独立的。例如,在文本文件中,字母后面有一个小字母的可能性比字母后面有空格或标点符号的可能性要大得多,因为单词通常长于 2 个字符。因此,下一个字符在 az 范围内的概率与前一个字符的值相关。不要对任何真实数据使用 Nick 的粗略估计,而是使用 gzip 压缩比。

于 2013-12-31T13:02:26.677 回答