簡體   English   中英

SQL中二進制字符串的漢明距離

[英]Hamming distance on binary strings in SQL

我的數據庫中有一個表,我將 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) ,其中 ^ 是按位異或運算符,而 BIT_COUNT 返回二進制字符串中 1 的數量)。

現在,我知道 ^ 運算符和 BIT_COUNT 函數都只適用於整數,所以我想說可能唯一的方法是分解子字符串中的二進制字符串,將每個二進制子字符串轉換為整數,計算漢明距離子串,然后將它們相加。 問題在於它聽起來非常復雜,效率不高,而且絕對不優雅。 因此,我的問題是:你能提出更好的方法嗎? (請注意,我在共享主機上,因此我無法修改數據庫服務器或加載庫)

編輯(1):顯然可以在 PHP 中加載整個表並在那里進行計算,但我寧願避免它,因為該表可能會變得非常大。

編輯(2):數據庫服務器是 MySQL 5.1

編輯(3):我下面的回答包含我剛剛在上面描述的代碼。

編輯(4):我剛剛發現使用 4 個 BIGINT 來存儲散列而不是 BINARY(32) 會產生巨大的速度改進(快 100 倍以上)。 請參閱下面對我的回答的評論。

將數據存儲在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,這是我在解釋問題時暗示的代碼。 歡迎使用更好的方法來完成同樣的事情(我特別不喜歡二進制 > 十六進制 > 十進制轉換):

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)
  );

有趣的問題,我找到了一種對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刪除任何全零,余數的長度是 1 的數量。 (轉換為二進制會忽略前導零,因此計數零不起作用。)

這將打印6 ,它與

0xAAAAAA ^ 0x888888 = 0x222222 = 0b1000100010001000100010

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM