hammingDistance


分享一下大作业优化的技巧

__uint128_t_

最近越来越多的协议会定义 16 字节长的整形,gcc 在 4.6 以上版本就可以使用 __int128_t & __uint128_t 了。

但需要注意的是,_uint128_t & __int128_t 仅对 64 位程序才有定义,因此如果编译选项中加入了 -m32,会出现找不到定义的编译错误。

另外 _uint128_t & __int128_t 并非 c/c++ 标准,所以 gcc 目前只支持基本运算符的操作,printf 这些都需要另外实现。

快速计算汉明距离

在信息论中,两个等长字符串之间的汉明距离(英语:Hamming distance)是两个字符串对应位置的不同字符的个数。换句话说,它就是将一个字符串变换成另外一个字符串所需要替换的字符个数。

汉明距离是以理查德・卫斯里・汉明的名字命名的。在通信传输过程中,累计定长二进制字中发生翻转的错误数据位,所以它也被称为信号距离。汉明距离在包括信息论、编码理论、密码学等领域都有应用。

对于两个字符串计算汉明距离,那肯定也就只能是逐位比较

//s1,s2为两个字符串,M为这两个字符串的长度
int hammingDistance(char s1[], char s2[], int M) {
    int dist = 0;
    int l1 = strlen(s1), l2 = strlen(s2);
    for (int i = 0; i < M && i < l1 && i < l2; ++i) {
        dist += s1[i] ^ s2[i];
    }
    return dist;
}

那么这样肯定是很慢的,如果可以转化两个整型的数,我们很容易想到利用到异或运算。

__uint128_t fingerprint = 0;
for(int l = 0; l < M; ++l){
    if(sumsignedweight > 0){
        fingerprint = (fingerprint << 1) | 1;
    }else{
        fingerprint = (fingerprint << 1) | 0;
    }
}

这样可以转换最高128位的两个01字符串。

那么就可以利用^运算来就算汉明距离了,由于异或运算的性质,得到的c的数位上为1说明ab的对应位不相同,反之则相同,那么我们就之需要计算c的二进制表示中数位为1的数目。这里可以按位采取&运算。

int hammingDistance(__uint128_t a,__uint128_t b){
    __uint128_t c = a ^ b;
    int dist = 0;
    while(c){
        if(c & 1){
            dist += 1;
        }
        c = c >> 1;
    }
    return dist;
}

布莱恩·克尼根算法

我们先观察如下一个现象:对于任意一个非零的二进制数 a(将其看作无符号数),考虑 aa-1 的关系。由于 a 非零,那么 a 中总有一些位为 1。假设 a 中最低位的 1 处于从右向左数的第 N 位。那么,a 的第 N 位以及第 N 位以后的每一位的值和 a-1 的第 N 位及第 N 位以后的每一位的值均不同。

举个例子就很容易理解了。我们以 8 位数来描述。假设 a=10010000,根据上述描述,从右往左数的第一个 1 出现在第 5 位,那么有 N=5。同时可以计算出 a-1=10001111,可以看到,从第 N 位开始,a 的后缀是 10000,而 a-1 的后缀是 01111。满足上述描述的现象。

进一步地,我们可以发现,如果对 aa-1 进行与操作,就会直接消去位于最后一位,也就是第 N 位的 1。还以上面的 a 为例,a & (a-1)=10000000。可以看到,我们不需要遍历,而是通过一次运算,就可以把 a 中的最后一个 1 消掉。如果我们一直重复这项操作,那么 a 里有多少个 1,我们就仅需要多少次 a & (a-1) 的操作,就能把 a 化为 0 了。而这个操作的次数正是我们所要求的。

那么计算汉明距离就可以改进为:

int hammingDistance(__uint128_t a,__uint128_t b){
    __uint128_t c = a ^ b;
    int dist = 0;
    while(c){
        dist += 1;
        c = c & (c - 1);
    }
    return dist;
}

文章作者: hugo
版权声明: 本博客所有文章除特別声明外,均采用 CC BY 4.0 许可协议。转载请注明来源 hugo !
  目录