如何计算文件的熵?

时间:2009-06-13 10:23:13

标签: algorithm file-io entropy

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

我的想法如下:

  • 创建一个包含256个整数(全为零)的数组。
  • 遍历文件及其每个字节,
    增加数组中的相应位置。
  • 最后:计算数组的“平均值”。
  • 用零初始化计数器,
    并为每个阵列的条目:
    添加条目的差异 “平均”到柜台。
嗯,现在我被卡住了。如何以这种方式“计划”计数器结果 所有结果都介于0.0和1.0之间?但我敢肯定, 无论如何,这个想法是不一致的......

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

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

12 个答案:

答案 0 :(得分:47)

  
      
  • 最后:计算数组的“平均值”。
  •   
  • 将计数器初始化为零,      并为每个数组的条目:      将条目的差异添加到“平均”到柜台。
  •   

通过一些修改,您可以获得Shannon的熵:

将“average”重命名为“entropy”

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

答案 1 :(得分:30)

要计算字节集合的信息熵,您需要执行与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和日志( p )将未定义(“负无穷大”),从而导致错误。

  • 256调用中的math.log表示可能的离散值的数量。由8位组成的字节将具有256个可能的值。

结果值将介于0(文件中的每个字节相同)之间,最多为1(字节在字节的每个可能值之间均匀分配)。


使用log base 256的解释

确实,这个算法通常使用log base 2来应用。这样就得到了比特的结果答案。在这种情况下,对于任何给定文件,您最多有8位熵。亲自尝试:通过byte_counts列出所有12100的列表,最大化输入的熵。当文件的字节均匀分布时,您会发现存在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之间的值

答案 2 :(得分:29)

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

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

答案 3 :(得分:17)

对于它的价值,这里是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;
}

答案 4 :(得分:14)

这是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.
...

答案 5 :(得分:12)

我回答的时间已经迟了两年了,所以请尽管只有少数选票才能考虑这个问题。

简短回答:使用下面的第1和第3个粗体公式来了解大多数人在说“熵”时所考虑的内容。一个文件的位数。如果你想要Shannon的H熵,那么只使用第一个等式,这实际上是熵/符号,因为他在他的论文中陈述了13次,这是大多数人都不知道的。一些在线熵计算器使用这个,但Shannon的H是特定的熵&#34;而不是&#34;总熵&#34;这引起了很多困惑。如果你想要0到1之间的答案,使用第一和第二个等式,它是归一化的熵/符号(它不是比特/符号,而是数据的真实统计量度&#34;熵性质&#34;通过让数据选择自己的日志库而不是任意分配2,e或10)。

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

  • 香农(特定)熵 H = -1 *总和(count_i / N * log(count_i / N))
    其中count_i是符号i在N中出现的次数 如果log是base 2,则单位是位/符号,如果是自然日志,则单位是nats /符号。
  • 归一化特定熵: H / log(n)
    单位是熵/符号。范围从0到1. 1表示每个符号经常发生,接近0表示除1之外的所有符号只出现一次,而非常长的文件的其余部分是另一个符号。该日志与H的基础相同。
  • 绝对熵 S = N * H
    如果log是base 2,则单位是位,如果是ln(),则为nat。
  • 归一化绝对熵 S = N * H / log(n)
    单位是&#34; entropy&#34;,从0到N不等。日志与H的基数相同。

虽然最后一个是最真实的&#34;熵&#34;,但是第一个(香农熵H)是所有书籍所称的&#34;熵&#34;没有(必要的恕我直言)资格。大多数人都没有澄清(像香农那样)每个符号的比特/符号或熵。呼叫H&#34;熵&#34;说得太松散了。

对于每个符号频率相等的文件:S = N * H = N.大多数大型文件都是这种情况。熵不对数据进行任何压缩,因此完全不知道任何模式,因此000000111111具有与010111101000相同的H和S(在这两种情况下都是6 1&6;并且6 0&#39;)

正如其他人所说的那样,使用像gzip这样的标准压缩程序并在之前和之后进行分割将更好地衡量预先存在的&#34;顺序&#34;在文件中,但是这偏向于更适合压缩方案的数据。没有通用的完美优化压缩器,我们可以使用它来定义绝对&#34;顺序&#34;。

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

H是一个强化的&#34;熵,即每个符号类似于物理学中的特定熵,其是每kg或每摩尔的熵。定期&#34;广泛&#34;类似于物理学的文件的熵。 S是S = N * H,其中 N 是文件中的符号数。 H完全类似于理想气体体积的一部分。信息熵不能简单地在更深层意义上与物理熵完全相等,因为物理熵允许&#34;有序&#34;以及无序排列:物理熵不仅仅是一个完全随机的熵(如压缩文件)。不同的一个方面对于理想气体,有另外的5/2因子来解释这个:S = k * N *(H + 5/2)其中H =每分子可能的量子态=(xp)^ 3 / hbar * 2 * sigma ^ 2其中x =盒子的宽度,p =系统中的总非定向动量(根据动能和每分子质量计算),sigma = 0.341,与不确定性原理一致,仅给出数量1 std dev中的可能状态。

一个小数学为文件提供了一种较短形式的标准化广义熵:

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

这个单位是&#34; entropy&#34; (这不是一个单位)。它被归一化为比熵&#34;更好的通用度量。 N * H的单位。但它也不应被称为&#34; entropy&#34;没有澄清,因为正常的历史惯例是错误地称H&#34; entropy&#34; (这与香农的文字中的澄清相反)。

答案 6 :(得分:11)

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

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

答案 7 :(得分:5)

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

或者,如果文件的内容是unicode字符,则应使用那些

答案 8 :(得分: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;
 }

答案 9 :(得分:2)

Re:我需要整个事情来对文件的内容做出假设: (明文,标记,压缩或一些二进制,......)

正如其他人指出的那样(或被混淆/分心),我认为你实际上是在谈论度量熵(熵除以消息的长度)。请参阅Entropy (information theory) - Wikipedia

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

enter image description here 资料来源:https://github.com/cyphunk/entropy_examples。 [请注意,此图表和其他图表可通过小说http://nonwhiteheterosexualmalelicense.org许可证获取.... ]

更有趣的是Analysing the byte entropy of a FAT formatted disk | GL.IB.LY

的分析和类似图表

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

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

答案 10 :(得分:0)

这是一个基于此 snippet 和无限战争期间发生的入侵的 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 字节而不是 5412 字节)。

答案 11 :(得分:-1)

没有任何附加信息,文件的熵(按定义)等于其大小* 8位。文本文件的熵大致为大小* 6.6位,假设:

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

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

一般来说,你不能谈论给定文件的熵。熵是文件集的属性

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

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