汉明距离

本文转载自文章“汉明距离及其高效计算方法”,记录笔记。

汉明距离

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

对于两个数字来说,汉明距离就是转成⼆进制后,对应的位置值不相同的个数。例如,假设有两个⼗进制数 a=93 和 b=73 ,如果将这两个数⽤⼆ 进制表⽰的话,有 a=1011101 、 b=1001001 ,可以看出,⼆者的从右往左数的第3位、第5位不同(从1开始数),因此, a 和 b 的汉明距离是2

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

  • 信号距离,就是传输前后信息若发生翻转,出现错误,则出现的错误数据位数是信号距离。

如何计算

既然⽬标是计算两个⼆进制数的对应位的值不同的个数,我们⾃然会联想到异或运算。因为异或运算的原则就是相同为0,不同为1。因此,通过 计算 \(c = a XOR b\) ,然后统计\(c\)中的各⼆进制位出现的 1 的次数,就能得到汉明距离了。

例如:\(a XOR b=(1011101)_2 XOR (1001001)_2= (0010100)_2\),汉明距离是2

一眼就能看出\((0010100)_2\)中有2个1,那计算机怎么识别呢?

即如何统计 \(c\) 的⼆进制格式中 1 出现的次数?

方法1

我们可以将 \(c\) 逐步右移,并且每次将其和 1 (假设位宽是8,也就是 00000001 )进⾏与运算,以检测最右边的位是否为1(如果最右边的位是 1 ,那么与运算的结果肯定也是1 ,否则为 0 )。⼀个循环下来,就能检测出 \(c\) 中 1 出现的次数了。

例如:

image-20220906204800912

程序实现:

def hammingDistance(x, y):
    xor = x ^ y #先异或
    distance = 0
    # 每次右移,最左边都会补零,因此截⽌条件是xor已经是⼀个零值了
    while xor:
        if xor & 1:
        	distance = distance + 1 #检测到1
        xor = xor >> 1 # 右移
    return distance

方法2

上述算法是⼀个很符合直觉的算法,但需要遍历所有的位。这⾥给出⼀个更精巧的思路,可以提⾼性能。

布莱恩.尼克根算法

我们先观察如下⼀个现象:对于任意⼀个⾮零的⼆进制数 a (将其看作⽆符号数),考虑 a 和 a-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 。满⾜上述描述的现象。

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

再举例看⼀下这个过程。假设⼀个数 x=10010001 ,那么有:

x = 10010001, x-1 = 10010000, y = x & (x - 1) = 10010000

y = 10010000, y-1 = 10001111, z = y & (y - 1) = 10000000

z = 10000000, z-1 = 01111111, z & (z - 1) = 00000000

x ⾥有 3 个 1 ,经过上述过程,只需要 3 次操作,就能得知 x 中有多少个 1 。⽽如果使⽤遍历的⽅法的话,需要 8 次操作才⾏。

改进的求汉明距离的代码如下:

def hammingDistance(x, y):
    xor = x ^ y #先异或
    distance = 0
    while xor:
        distance = distance + 1
        xor = xor & (xor - 1)
    return distance

这也是leetcode上的⼀道热题#461,⼤家可以参考。

image-20220906211956980

  • 可以看出方法2更快。

image-20220906212549507

posted @ 2022-09-06 21:32  PamShao  阅读(1489)  评论(0编辑  收藏  举报