快速求熵程序

信息熵很有用,就拿我的老本行反病毒来说,它是静态病毒特征的常见组成部分。信息熵的计算公式很简单:

$ Entropy=-\sum_{i=1}^{n}P(X_{i})log_{2}P(X_{i}) $

其中,$ P(X_{i}) $是随机变量$X_{i}$出现的概率,这里熵的单位是bit。通常,我们用$C(X_{i})$表示$X_{i}$出现的次数,$T=\sum_{i=1}^{n}C(X_{i})$表示观察次数的总和,那么:

$ Entropy=-\sum_{i=1}^{n}\frac{C(X_{i})}{T}log_{2}\frac{C(X_{i})}{T} $

根据这个公式,最直接也是最常见的求熵程序类似于:

double entropy(int *counts, int n, int total)
{
    double sum = 0;
    double p;
    for(int i = 0; i < n; i++){
        if(counts[i] > 0){
            p = (double) counts[i] / total;
            sum -= p * log(p);
        }
    }
    return sum / log(2.0);
}

比如,要计算某段内存数据的熵,一般是这样调用entropy函数的:

double entropy_data(unsigned char *data, int size)
{
    int counts[256];
    memset(counts, 0, sizeof(int) * 256);
    for(int i = 0; i < size; i++)
        counts[data[i]]++;
    return entropy(counts, 256, size);
}

如果内存数据里的字节是均匀分布的(比如随机字节序列),那么熵值接近于8。反之,某几个字节大量重复出现时,熵值接近于0。在病毒特征中,特定文件数据的熵值是判断文件是否经过压缩加密的重要指标。

在实际应用中,我们对熵的精度要求并不高,15个有效位的double乃至7个有效位的float都显得有些“浪费”,而浮点运算又使得熵的计算速度不敢恭维。那么,是否可以在牺牲部分精度的前提下,快速计算近似熵值呢?答案是肯定的。经过几天的琢磨,终于写了一个出来。

在贴出代码之前,先看一下计算100万次熵值的用时对比数据(单位:ms)。测试环境:i7-3520M @2.9GHz, 8GB RAM, Win7 SP1 64-bit。

  x86 编译 x64 编译
entropy 4575.6 9851.5
fast_entropy 677.1 405.6

x86版本的fast_entropy速度大约是entropy的6倍,x64版本的fast_entropy速度是entropy的24倍。结果精度方面,fast_entropy一般可以精确至小数点后3到4位,严格的误差分析还没有做,有时间会补上。

fast_entropy用了整数运算来替代浮点运算,并应用了IEEE浮点数存储格式的技巧,得以用整数运算近似Log运算。之所以在x64下表现更好,是因为64-bit整数操作在x64上更快。

好了,上代码!

static int _u[256] = {
    0xc0801c36, 0xc0805429, 0xc0808b65, 0xc080c1eb, 0xc080f7bf, 0xc0812cdf, 0xc081614f, 0xc081950f,
    0xc081c821, 0xc081fa86, 0xc0822c3f, 0xc0825d4d, 0xc0828db3, 0xc082bd71, 0xc082ec88, 0xc0831af9,
    0xc08348c7, 0xc08375f1, 0xc083a27a, 0xc083ce62, 0xc083f9ab, 0xc0842455, 0xc0844e63, 0xc08477d4,
    0xc084a0aa, 0xc084c8e6, 0xc084f08a, 0xc0851796, 0xc0853e0c, 0xc08563ec, 0xc0858937, 0xc085adef,
    0xc085d215, 0xc085f5a9, 0xc08618ad, 0xc0863b21, 0xc0865d07, 0xc0867e60, 0xc0869f2c, 0xc086bf6c,
    0xc086df22, 0xc086fe4e, 0xc0871cf2, 0xc0873b0e, 0xc08758a2, 0xc08775b1, 0xc087923b, 0xc087ae40,
    0xc087c9c2, 0xc087e4c1, 0xc087ff3f, 0xc088193c, 0xc08832b9, 0xc0884bb7, 0xc0886436, 0xc0887c38,
    0xc08893bd, 0xc088aac6, 0xc088c155, 0xc088d768, 0xc088ed03, 0xc0890224, 0xc08916cd, 0xc0892aff,
    0xc0893ebb, 0xc0895200, 0xc08964d1, 0xc089772d, 0xc0898916, 0xc0899a8b, 0xc089ab8e, 0xc089bc20,
    0xc089cc41, 0xc089dbf2, 0xc089eb34, 0xc089fa06, 0xc08a086b, 0xc08a1662, 0xc08a23ec, 0xc08a310a,
    0xc08a3dbc, 0xc08a4a04, 0xc08a55e1, 0xc08a6155, 0xc08a6c60, 0xc08a7702, 0xc08a813d, 0xc08a8b10,
    0xc08a947d, 0xc08a9d84, 0xc08aa626, 0xc08aae62, 0xc08ab63b, 0xc08abdb0, 0xc08ac4c2, 0xc08acb71,
    0xc08ad1be, 0xc08ad7aa, 0xc08add35, 0xc08ae260, 0xc08ae72b, 0xc08aeb97, 0xc08aefa4, 0xc08af353,
    0xc08af6a4, 0xc08af998, 0xc08afc30, 0xc08afe6b, 0xc08b004b, 0xc08b01d0, 0xc08b02fa, 0xc08b03ca,
    0xc08b0440, 0xc08b045e, 0xc08b0422, 0xc08b038f, 0xc08b02a4, 0xc08b0161, 0xc08affc8, 0xc08afdd9,
    0xc08afb94, 0xc08af8f9, 0xc08af609, 0xc08af2c5, 0xc08aef2d, 0xc08aeb42, 0xc08ae703, 0xc08ae271,
    0xc08add8e, 0xc08ad858, 0xc08ad2d1, 0xc08accf9, 0xc08ac6d0, 0xc08ac057, 0xc08ab98e, 0xc08ab276,
    0xc08aab0f, 0xc08aa35a, 0xc08a9b56, 0xc08a9305, 0xc08a8a66, 0xc08a817a, 0xc08a7841, 0xc08a6ebd,
    0xc08a64ec, 0xc08a5ad0, 0xc08a5069, 0xc08a45b8, 0xc08a3abc, 0xc08a2f76, 0xc08a23e6, 0xc08a180d,
    0xc08a0bec, 0xc089ff81, 0xc089f2cf, 0xc089e5d5, 0xc089d893, 0xc089cb0a, 0xc089bd3b, 0xc089af25,
    0xc089a0c8, 0xc0899227, 0xc089833f, 0xc0897413, 0xc08964a2, 0xc08954ec, 0xc08944f2, 0xc08934b5,
    0xc0892434, 0xc089136f, 0xc0890268, 0xc088f11f, 0xc088df93, 0xc088cdc5, 0xc088bbb6, 0xc088a965,
    0xc08896d4, 0xc0888401, 0xc08870ef, 0xc0885d9c, 0xc0884a09, 0xc0883637, 0xc0882226, 0xc0880dd5,
    0xc087f946, 0xc087e479, 0xc087cf6e, 0xc087ba24, 0xc087a49e, 0xc0878eda, 0xc08778d9, 0xc087629b,
    0xc0874c21, 0xc087356b, 0xc0871e78, 0xc087074b, 0xc086efe1, 0xc086d83d, 0xc086c05e, 0xc086a844,
    0xc0868ff0, 0xc0867762, 0xc0865e9a, 0xc0864598, 0xc0862c5e, 0xc08612ea, 0xc085f93d, 0xc085df58,
    0xc085c53a, 0xc085aae4, 0xc0859057, 0xc0857592, 0xc0855a95, 0xc0853f61, 0xc08523f7, 0xc0850855,
    0xc084ec7e, 0xc084d070, 0xc084b42c, 0xc08497b2, 0xc0847b03, 0xc0845e1e, 0xc0844105, 0xc08423b6,
    0xc0840633, 0xc083e87c, 0xc083ca90, 0xc083ac71, 0xc0838e1d, 0xc0836f96, 0xc08350dc, 0xc08331ee,
    0xc08312ce, 0xc082f37b, 0xc082d3f5, 0xc082b43d, 0xc0829453, 0xc0827437, 0xc08253ea, 0xc082336b,
    0xc08212ba, 0xc081f1d9, 0xc081d0c7, 0xc081af83, 0xc0818e10, 0xc0816c6c, 0xc0814a98, 0xc0812894,
    0xc0810660, 0xc080e3fd, 0xc080c16b, 0xc0809ea9, 0xc0807bb8, 0xc0805899, 0xc080354a, 0xc08011ce,
};

static inline long long _lxlogx(int x)
{
    float f = (float)x;
    int i = *(int *)&f;
    i += _u[(i & 0x007F8000) >> 15];
    return (long long)i * x;
}

double fast_entropy(int *counts, int n, int total)
{
    long long s = 0;
    int i, c;
    for(i = 0; i < n; i++) {
        if(c = counts[i])
            s -= _lxlogx(c);
    }
    s += _lxlogx(total);
    s /= total;
    return 0.00000011920929 * s;
}
时间: 2024-12-09 22:56:13

快速求熵程序的相关文章

快速求熵程序(续)

这篇blog是对快速求熵程序的数学解释. 首先,还是从熵的计算公式出发,略做一些推导: $ Entropy=-\sum_{i=1}^{n}P(X_{i})log_{2}P(X_{i})$ $=-\sum_{i=1}^{n}\frac{C(X_{i})}{T}log_{2}\frac{C(X_{i})}{T}$ $=-\frac{1}{T}\sum_{i=1}^{n}C(X_{i})(log_{2}C(X_{i})-log_{2}T)$ $=-\frac{1}{T}(\sum_{i=1}^{n}C

快速求两个数组交集算法

快速求出两个数组的交集的算法,如果用循环遍历的方法,其时间复杂度为O(N^N),在面试中一般不考虑这种方法. 这里提供一种快速算法,算法实现步骤如下: 1. 找到arr1的最大数max,创建一个max+1大小的数组result. 2. 以arr1中的值作为result的下标,该索引处的值+1. 3. 在result数组中遍历arr2中的值为下标处的值,如果该索引值不为0,则代表该值是两者的交集,保留. 程序实现如下: /** * 求两个数组的交集 * @param arr1 * @param a

非递归快速求幂算法

快速求正整数次幂,当然不能直接死乘.举个例子:3 ^ 999 = 3 * 3 * 3 * … * 3直接乘要做998次乘法.但事实上可以这样做,先求出2^k次幂:3 ^ 2 = 3 * 33 ^ 4 = (3 ^ 2) * (3 ^ 2)3 ^ 8 = (3 ^ 4) * (3 ^ 4)3 ^ 16 = (3 ^ 8) * (3 ^ 8)3 ^ 32 = (3 ^ 16) * (3 ^ 16)3 ^ 64 = (3 ^ 32) * (3 ^ 32)3 ^ 128 = (3 ^ 64) * (3

A sequence of numbers(快速求幂)

题目描述 Xinlv wrote some sequences on the paper a long time ago, they might be arithmetic or geometric sequences. The numbers are not very clear now, and only the first three numbers of each sequence are recognizable. Xinlv wants to know some numbers in

NYOJ--102--次方求模(快速求幂取模)

次方求模 时间限制:1000 ms  |  内存限制:65535 KB 难度:3 描述 求a的b次方对c取余的值 输入 第一行输入一个整数n表示测试数据的组数(n<100)每组测试只有一行,其中有三个正整数a,b,c(1=<a,b,c<=1000000000) 输出 输出a的b次方对c取余之后的结果 样例输入 3 2 3 5 3 100 10 11 12345 12345 样例输出 3 1 10481 1 /* 2 Name: NYOJ--102--次方求模 3 Copyright: ?

快速求幂

快速幂在算指数时是很高效的,他的基本原理是二进制. 如果要算 2^5,可以直接2*2*2*2*2 但是如果要算 3^999,指数N太大,计算太慢,所以有一种快速的解法. @@@@@@@@@@@@@@@@@@@@@@@@ 以3^21为例. 2^21=(2^16)×(2^4)×(2^1) 21的二进制可以写成(10101)----------------而10101可以写成1*2^4+0*2^3+1*2^2+0*2^1+1*2^0 可以明显看出, 每一个香对应着上边的指数.   2^4=16 2^2

Quick-Select 1亿个数快速求第K小的数 分治法

Quick-Select  1亿个数快速求第K小的数  分治法 利用快速排序的思想,一开始选取中枢元,然后左右调整,接着比对中枢元p和K的大小,如果 p+1 = k (数组从0开始), 那么a[p] 就是答案,因为在p之前的,肯定都是小于a[p]的, 在p之后的,肯定大于p, 所以 a[p] 就是第 p+1 小.假如 p+1 不等于K, 那么根据大小,进行左右调整.调整过程中,理想状态下,每次都砍掉一半,数组的起始坐标要进行调整. 代码: // 快速排序法的修改 #include <iostre

POJ2478_Farey Sequence【快速求欧拉函数】

Farey Sequence Time Limit: 1000MS Memory Limit: 65536K Total Submissions: 12377 Accepted: 4808 Description The Farey Sequence Fn for any integer n with n >= 2 is the set of irreducible rational numbers a/b with 0 < a < b <= n and gcd(a,b) = 1

快速求幂运算笔记

如何快速求x得n次方呢? 首先C++里面有个pow如何实现呢?自己查查,里面使用double,肯定更麻烦,还有jianzhi 我们会顺手写下 int res=1; for(int i=1;i<=n;i++) { res*=x; } 学习一下快速幂,logn内计算出来,使用N的二进制,只需要logN就可以计算. 正要的就是计算每个为1对应的基数.列入: 11是多少?是二进制的话,就是1*2^1+1 那么x^(11)呢,两个1对应的基数是多少呢?低位1对应的是X,高位1对应的是X^2,因此,就是位于