вычисление пересечения битовых флагов для конгруэнтности


Технические обстоятельства:

Дан столбец int в SQL Server 2008 R2 для сохранения десятичных" закодированных " битовых флагов (начиная от 20 чтобы 230, таким образом, имея 31 доступный флаг с максимумом 1073741824). Этот столбец может содержать весь диапазон выделяемых целочисленных комбинаций, все для классификации одного объекта.

Задача, подлежащая решению:

Из конечного количества десятичных "битовых целых чисел" мы должны найти один число, которое представляет собой своего рода пересечение всех этих флагов – например, когда у вас есть 3,10,524290, очевидно, что 2 должно быть установлено в результате, в то время как другие биты (1,8,524288) спорны. Это очень простой пример. Это может быть реальный набор входных данных (только первые два столбца):

Occurences | Decimal bit field | Binary representation
     7     |   268435460       | 10000000000000000000000000100
     5     |   268435488       | 10000000000000000000000100000
     5     |         128       | 00000000000000000000010000000
     4     |          32       | 00000000000000000000000100000
     3     |           4       | 00000000000000000000000000100
     3     |   268435492       | 10000000000000000000000100100
     2     |          36       | 00000000000000000000000100100
     2     |         132       | 00000000000000000000010000100
     1     |         160       | 00000000000000000000010100000
   Occurences of particular bit: 3--------------------3-6--6--
     Desired output possibility: 10000000000000000000000100100

Решение пока:

... реализовано в Transact-SQL:

  1. соберите все целые числа, подлежащие вычислению, и объедините их в запятую строка.
  2. сокращение строки, вынимание первого числа, циклическое перебирание:
  3. проверка испытуемого по максимальному значению (&-AND) и деление максимума на 2 (пока >=1).

... пока? о.*

Теперь у меня есть выход двоичных представителей для набора битов. Я рассматриваю возможность сохранения этих битов во временную таблицу с 31 столбцом, чтобы перейти к оценке. Но потом я подумал: а нет ли более умного способа сделать это? SQL Server работает очень быстро, даже когда разборка 10000 сгенерированных целых чисел. Но, возможно, есть встроенная функция для вычисления "расстояния" между двумя двоичными битовыми флагами.

Я признаю, что это сложная проблема, но я действительно вижу свет в конце туннеля, даже когда это нужно сделать косвенным путем. Это будет еще сложнее, так как утяжеление также должно быть применено позже, так что 2× 00010000 @ 100% значение >00010000 @ 40% значение. Но я постараюсь разобраться с этим. это когда у меня есть сводка, сгенерированная и доступная ;-)
1 4

1 ответ:

Вы можете использовать побитовое и для ваших операций

declare @i int
declare @x int
declare @cnt int
select @i=2147483647  -- 1111111 11111111 11111111 11111111
declare @t table (a int)
insert into @t values( 3),(10),(524290);

Select @i= (@i & a) from @t 


-- just for fun an output
set @x=1
set @cnt=0
While @cnt<31
  begin
  Print Case when @x & @i <> 0 then 'X' else ' ' end  +' ' + Cast(@x as Varchar(12))   
  Set @cnt=@cnt + 1
  if @cnt<31  Set @x=@x*2
  end 

Или с более приятным выходом

declare @i int
declare @x int
declare @cnt int
select @i=2147483647  -- 1111111 11111111 11111111 11111111
Declare  @ref table(ref int) 
set @x=1
set @cnt=0
While @cnt<31
  begin
  insert into @ref Values(@x)
  Set @cnt=@cnt + 1
  if @cnt<31  Set @x=@x*2
  end 


declare @t table (a int)
insert into @t values( 3),(10),(524290);

Select @i= (@i & a) from @t 

Select * from @ref where ref&@i<>0 

Как ответ на ваш комментарий

declare @i int
declare @x int
declare @cnt int
select @i=2147483647  -- 1111111 11111111 11111111 11111111
Declare  @ref table(ref int) 
set @x=1
set @cnt=0
While @cnt<31
  begin
  insert into @ref Values(@x)
  Set @cnt=@cnt + 1
  if @cnt<31  Set @x=@x*2
  end 


declare @t table (a int)
insert into @t values( 3),(5),(9),(17),(33),(65),(128);

Select @i= (@i & a) from @t 

Select a,Count(*) as BitCount
from @ref r
join @t t on t.a & r.ref<>0
group by a

Select ref,Count(*) as OCC from @ref r
join @t t on t.a & r.ref<>0
group by ref

Select ref,(Select count(*) from @t where a & r.ref<>0) as OCC
from @ref r