SQL中二进制字符串的汉明距离

时间:2011-01-23 22:45:32

标签: sql mysql hash binary-data hamming-distance

我的数据库中有一个表,我将SHA256哈希存储在BINARY(32)列中。我正在寻找一种方法来计算列中条目的汉明距离到提供的值,例如:

SELECT * FROM table 
  ORDER BY HAMMINGDISTANCE(hash, UNHEX(<insert supplied sha256 hash here>)) ASC 
  LIMIT 10

(如果您想知道,字符串A和B的汉明距离定义为BIT_COUNT(A^B),其中^是按位XOR运算符,BIT_COUNT返回二进制字符串中1的数量)。

现在,我知道^运算符和BIT_COUNT函数都只能在INTEGER上运行,所以我想说可能唯一的方法就是分解子字符串中的二进制字符串,将每个二进制子字符串转换为整数,以子串方式计算汉明距离,然后加上它们。这个问题是它听起来非常复杂,效率不高,绝对不优雅。因此,我的问题是:你能提出更好的建议吗? (请注意我在共享主机上,因此我无法修改数据库服务器或加载库)

编辑(1):显然在PHP中加载整个表并进行计算是可能的,但我宁愿避免它,因为这个表可能会变得非常大。

edit(2):数据库服务器是MySQL 5.1

编辑(3):我的答案包含我刚才描述的代码。

编辑(4):我刚刚发现使用4个BIGINT来存储哈希而不是BINARY(32)会产生大量的速度提升(速度提高100倍以上)。请参阅下面的回答评论。

2 个答案:

答案 0 :(得分:14)

似乎将数据存储在BINARY列中是一种必然表现不佳的方法。获得良好性能的唯一快捷方法是将BINARY列的内容拆分为多个BIGINT列,每列包含原始数据的8字节子字符串。

在我的情况下(32字节),这意味着使用4 BIGINT列并使用此功能:

CREATE FUNCTION HAMMINGDISTANCE(
  A0 BIGINT, A1 BIGINT, A2 BIGINT, A3 BIGINT, 
  B0 BIGINT, B1 BIGINT, B2 BIGINT, B3 BIGINT
)
RETURNS INT DETERMINISTIC
RETURN 
  BIT_COUNT(A0 ^ B0) +
  BIT_COUNT(A1 ^ B1) +
  BIT_COUNT(A2 ^ B2) +
  BIT_COUNT(A3 ^ B3);

在我的测试中,使用这种方法比使用BINARY方法快100多倍。


FWIW,这是我在解释问题时所暗示的代码。欢迎更好的方法来完成同样的事情(我特别不喜欢二进制&gt;十六进制&gt;十进制转换):

CREATE FUNCTION HAMMINGDISTANCE(A BINARY(32), B BINARY(32))
RETURNS INT DETERMINISTIC
RETURN 
  BIT_COUNT(
    CONV(HEX(SUBSTRING(A, 1,  8)), 16, 10) ^ 
    CONV(HEX(SUBSTRING(B, 1,  8)), 16, 10)
  ) +
  BIT_COUNT(
    CONV(HEX(SUBSTRING(A, 9,  8)), 16, 10) ^ 
    CONV(HEX(SUBSTRING(B, 9,  8)), 16, 10)
  ) +
  BIT_COUNT(
    CONV(HEX(SUBSTRING(A, 17, 8)), 16, 10) ^ 
    CONV(HEX(SUBSTRING(B, 17, 8)), 16, 10)
  ) +
  BIT_COUNT(
    CONV(HEX(SUBSTRING(A, 25, 8)), 16, 10) ^ 
    CONV(HEX(SUBSTRING(B, 25, 8)), 16, 10)
  );

答案 1 :(得分:1)

有趣的问题,我找到了一种方法,可以为binary(3) binary(32)找到一个可能同样适用于drop table if exists BinaryTest; create table BinaryTest (hash binary(3)); insert BinaryTest values (0xAAAAAA); set @supplied = cast(0x888888 as binary); select length(replace(concat( bin(ascii(substr(hash,1,1)) ^ ascii(substr(@supplied,1,1))), bin(ascii(substr(hash,2,1)) ^ ascii(substr(@supplied,2,1))), bin(ascii(substr(hash,3,1)) ^ ascii(substr(@supplied,3,1))) ),'0','')) from BinaryTest; 的方法:

replace

6删除任何全零,余数的长度是1的数量。 (转换为二进制会省略前导零,因此计算零将无效。)

这会打印0xAAAAAA ^ 0x888888 = 0x222222 = 0b1000100010001000100010 ,与

中的数量相匹配
{{1}}