Вес Хэмминга / количество населения в T-SQL

Я ищу быстрый способ вычислить вес Хэмминга / количество населения /"число 1 бит" поля BINARY(1024). MySQL имеет функцию BIT_COUNT, которая делает что-то подобное. Я не мог найти аналогичную функцию в T-SQL?

Или вы бы предложили хранить двоичные данные в поле другого типа?

Если вы не знаете, о чем я говорю, вот статья в Википедии о весе Хэмминга.

5 ответов

Решение

Вы можете использовать вспомогательную таблицу с предварительно вычисленными весами Хэмминга для небольших чисел, например, байтов, затем соответствующим образом разделить значение, присоединиться к вспомогательной таблице и получить сумму частичных весов Хемминга в качестве веса Хэмминга значения:

-- define Hamming weight helper table
DECLARE @hwtally TABLE (byte tinyint, hw int);
INSERT INTO @hwtally (byte, hw) VALUES (0, 0);
INSERT INTO @hwtally (byte, hw) SELECT   1 - byte, 1 - hw FROM @hwtally;
INSERT INTO @hwtally (byte, hw) SELECT   3 - byte, 2 - hw FROM @hwtally;
INSERT INTO @hwtally (byte, hw) SELECT   7 - byte, 3 - hw FROM @hwtally;
INSERT INTO @hwtally (byte, hw) SELECT  15 - byte, 4 - hw FROM @hwtally;
INSERT INTO @hwtally (byte, hw) SELECT  31 - byte, 5 - hw FROM @hwtally;
INSERT INTO @hwtally (byte, hw) SELECT  63 - byte, 6 - hw FROM @hwtally;
INSERT INTO @hwtally (byte, hw) SELECT 127 - byte, 7 - hw FROM @hwtally;
INSERT INTO @hwtally (byte, hw) SELECT 255 - byte, 8 - hw FROM @hwtally;

-- calculate
WITH split AS (
  SELECT SUBSTRING(@value, number, 1) AS byte
  FROM master.dbo.spt_values
  WHERE type = 'P' AND number BETWEEN 1 AND LEN(@value)
)
SELECT
  Value = @value,
  HammingWeight = SUM(t.hw)
FROM split s
  INNER JOIN @hwtally t ON s.byte = t.byte

Когда вы играете с меньшим значением (что-то вроде 16-битного макс.), Самый эффективный способ сделать это с SQL Server - это использовать таблицу со всеми вычисленными результатами и использовать объединение.

Я ускорил запрос с 30 секунд до 0 секунд, выполняя подобные операции с запросом, который должен вычислять вес Хэмминга для 4-битного значения на 17 000 строк.

WITH HammingWeightHelper AS (
        SELECT  x, Fx 
        FROM (VALUES(0,0),(1,1),(2,1),(3,2),
                    (4,1),(5,2),(6,2),(7,3),
                    (8,1),(9,2),(10,2),(11,3),
                    (12,2),(13,3),(14,3),(15,4)) AS HammingWeight(x, Fx)
    )
SELECT HammingWeight.Fx As HammingWeight, SomeTable.Value As bitField
FROM   SomeTable INNER JOIN
       HammingWeightHelper ON HammingWeightHelper.x = SomeTable.Value 

Конечно, это уродливое решение, и оно, вероятно, не подойдет для длинной битовой области.

SQL Server, начиная с SQL Server 2022 CTP 2.1, поддерживает BIT_COUNT(). Документация здесь .

Не нашел ничего конкретно о весе Хэмминга, но вот что касается расстояния Хэмминга:

create function HamDist(@value1 char(8000), @value2 char(8000))
returns int
as
begin
    declare @distance int
    declare @i int
    declare @len int

    select @distance = 0,
           @i =1,
           @len = case when len(@value1) > len(@value2)
                       then len(@value1)
                       else len(@value2) end

    if (@value1 is null) or (@value2 is null)
        return null

    while (@i <= @len)
        select @distance = @distance +
                           case when substring(@value1,@i,1) != substring(@value2,@i,1)
                                then 1
                                else 0 end,
               @i = @i +1

    return @distance
end

Это вычисляет расстояние Хэмминга между двумя значениями. Вес Хэмминга одного значения будет расстоянием Хэмминга между этим значением и массивом нулевых значений.

Я не мог найти хороший способ сделать это. В конце я вычислил вес Хемминга в Java и периодически обновлял количество битов в базе данных.

Другие вопросы по тегам