Вес Хэмминга / количество населения в 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 и периодически обновлял количество битов в базе данных.