Как узнать, сколько бит включено в число - программирование
Подтвердить что ты не робот

Как узнать, сколько бит включено в число

Я знаю, как узнать, сколько бит включено в заданное число (или сколько элементов истинно в логической arra), используя маску и побитовые операторы, переходя все проверки битов, если они включены. Предполагая, что число имеет произвольную длину, алгоритм работает в O (n) времени, где n - количество бит в числе. Есть ли асимптотически лучший алгоритм? Я не думаю, что это возможно, , но как я могу официально доказать это?

4b9b3361

Ответ 1

Я думаю, что тип формальности, который вы ищете, является "состязательным доказательством".

Предположим, что у нас есть алгоритм A, который работает быстрее, чем O (n). Тогда при достаточно большом n A не должен смотреть на некоторый бит i. Затем мы утверждаем, что A должно быть неверным. "Противник" даст две строки s1 и s2, которые идентичны, за исключением противоположных значений бит i. Алгоритм A вернет то же значение для s1 и s2, поэтому противник "обманул" A, давая неправильный ответ. Таким образом, не существует правильного алгоритма A, выполняющегося в o (n) времени.

Ответ 2

Бит Twiddling Hacks содержит ряд методов, в том числе:

Установленные счетные биты, способ Брайана Кернигана

unsigned int v; // count the number of bits set in v
unsigned int c; // c accumulates the total bits set in v
for (c = 0; v; c++)
{
  v &= v - 1; // clear the least significant bit set
}

Метод Брайана Кернигана проходит через столько итераций, сколько есть установить биты. Итак, если у нас есть 32-битное слово с только набором высоких бит, тогда он будет проходить один раз через цикл.

Примеры алгоритма в действии:

128 == 10000000 2, 1 бит установлен

128 & 127 ==   0    10000000 & 01111111 == 00000000

177 == 10110001 2, 4 бита

177 & 176 == 176    10110001 & 10110000 == 10110000
176 & 175 == 160    10110000 & 10101111 == 10100000
160 & 159 == 128    10100000 & 10011111 == 10000000
128 & 127 ==   0    10000000 & 01111111 == 00000000

255 == 11111111 2, 8 бит

255 & 254 == 254    11111111 & 11111110 == 11111110
254 & 253 == 252    11111110 & 11111101 == 11111100
252 & 251 == 248    11111100 & 11111011 == 11111000
248 & 247 == 240    11111000 & 11110111 == 11110000
240 & 239 == 224    11110000 & 11101111 == 11100000
224 & 223 == 192    11100000 & 11011111 == 11000000
192 & 191 == 128    11000000 & 10111111 == 10000000
128 & 127 ==   0    10000000 & 01111111 == 00000000

Что касается языкового агностического вопроса об алгоритмической сложности, невозможно сделать лучше, чем O (n), где n - количество бит. Любой алгоритм должен проверять все биты в числе.

Какая сложность в этом заключается, когда вы не будете осторожны в определении n и пусть n будет "числом команд смещения/маскировки бит" или некоторыми такими. Если n - это число бит, то даже простая битовая маска (&) уже является операцией O ( n).

Итак, можно ли это сделать лучше, чем O (n) бит-тесты? Нет.
Может ли это быть сделано меньше, чем O (n) операции add/shift/mask? Да.

Ответ 3

Я всегда использую это:

int
count_bits(uint32_t v)
{
        v = v - ((v >> 1) & 0x55555555);
        v = (v & 0x33333333) + ((v >> 2) & 0x33333333);
        return ((v + (v >> 4) & 0xf0f0f0f) * 0x1010101) >> 24;
}

Вы должны знать размер ваших целых чисел.

http://graphics.stanford.edu/~seander/bithacks.html#CountBitsSetParallel

Ответ 4

Брайан Кернханган алгоритм для подсчета 1 бит.

unsigned int v; // count the number of bits set in v
unsigned int c; // c accumulates the total bits set in v
for (c = 0; v; c++)
{
  v &= v - 1; // clear the least significant bit set
}

Прочтите этот и другие бит-трюки: Бит-скручивающиеся хаки.

Ответ 5

Самый быстрый способ сделать это вычисление - с массивом table edx [bl], где регистр bl содержит значение байта. Если число представляет собой один байт, то ответ представляет собой одну инструкцию:

 mov eax, [edx:bl]

Если число содержит в нем много байтов (скажем, массив, на который указывает ebp), то вы зацикливаете байты (где ecx - количество байтов в массиве, содержащем число):

    sub ecx, 1
    mov eax, 0
 DoNextByte:
    mov bl, [ebp:ecx]
    add eax, [edx:bl]
    test ecx, ecx
    jz Done:
    sub ecx, 1
    jmp DoNextByte:
 Done:
    ; answer is in eax

Это самый быстрый способ сделать это и будет быстрее любых математических вычислений. Обратите внимание, что инструкции сдвига в решении Art очень дорогостоящие CPU.

Проблема с решением Kernighan заключается в том, что даже при ручной кодировке в сборке он медленнее, чем мой алгоритм. Если он скомпилирован C, он, вероятно, сгенерирует много обращений к памяти, что замедлит его даже за счет большего количества тактовых циклов, которые требуются.

Обратите внимание, что если сопоставление byte-to-count вставляется рядом с этой инструкцией, тогда вся таблица данных будет находиться в кэше CPU, поэтому будет очень быстро. В этом случае программа C не будет близка (думаю, 20x медленнее или больше).

Ответ 6

Ну, вы также можете использовать таблицу поиска, содержащую # бит для каждого байта, и затем делить число на байты, добавляя значения поиска.

Он будет еще O (количество бит), но с небольшим коэффициентом.

Ответ 7

Хорошо, здесь, похоже, возникает некоторая путаница о статистике порядка, асимптотической нотации, "большой O".

Верно, что алгоритм Брайана Кернигана лучше с точки зрения количества операций. Тем не менее, неверно, что он асимптотически лучше.

Это можно увидеть из определения большого-O.

Напомним, что по определению функция O (f (n)), когда существует функция g (n) такая, что f (n) & le; k g (n), когда n растет достаточно большим.

Теперь давайте определим w как число бит, заданное в слове, и еще раз отметим, что время выполнения для одного слова, как было отмечено выше, является функцией количества установленных битов. Вызовите эту функцию c (w). Мы знаем, что существует фиксированная ширина слова, назовите ее ww; ясно для любого слова, 0 & le; c (w) & le; ww, и, конечно, худшим случаем является то, что c (w) = c (ww). Таким образом, время выполнения этого алгоритма в худшем случае n c (ww).

Таким образом, для n время выполнения - & le; n c (ww); то есть n & le; n c (ww), и, таким образом, по определению этот алгоритм имеет асимптотическое наихудшее время выполнения O (n).