Как посчитать количество бит в 32-разрядное целое число?

8 бит, представляющих число 7, выглядят следующим образом:

00000111

установлены три бита.

какие есть алгоритмы для определения количества бит в 32-разрядное целое число?

30 ответов


Это известно как 'Вес Хэмминга', 'popcount' или 'боковое сложение'.

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

некоторые процессоры имеют одну встроенную инструкцию для этого, а другие имеют параллельные инструкции, которые действуют на битовых векторах. Параллельные инструкции (например, x86 popcnt, на процессорах, где он поддерживается) почти наверняка будет быстрее. Некоторые другие архитектуры могут иметь медленный инструкции выполнены с петлей платформах, что тесты немного за цикл (источник).

предварительно заполненный метод поиска таблицы может быть очень быстрым, если ваш процессор имеет большой кэш и / или вы делаете много этих инструкций в узком цикле. Однако он может пострадать из-за расхода "кэш-Мисс", где процессор должен получить часть таблицы из основной памяти.

Если вы знаете, что ваши байты будут в основном 0 или 1, в основном, то есть очень эффективные алгоритмы для этих сценариев.

Я считаю, что очень хороший алгоритм общего назначения является следующим, известный как "параллельный" или "алгоритм SWAR переменной точности". Я выразил это на C-подобном псевдо-языке, вам может потребоваться настроить его для работы на определенном языке (например, используя uint32_t для C++ и >>> в Java):

int numberOfSetBits(int i)
{
     // Java: use >>> instead of >>
     // C or C++: use uint32_t
     i = i - ((i >> 1) & 0x55555555);
     i = (i & 0x33333333) + ((i >> 2) & 0x33333333);
     return (((i + (i >> 4)) & 0x0F0F0F0F) * 0x01010101) >> 24;
}

Это имеет лучшее наихудшее поведение любого из обсуждаемых алгоритмов, поэтому будет эффективно работать с любым шаблоном использования или ценности, которые вы бросаете в него.


этот алгоритм побитового SWAR может распараллеливаться одновременно в нескольких векторных элементах, а не в одном целочисленном регистре, для ускорения процессоров с SIMD, но без полезной инструкции popcount. (например, x86-64 код, который должен работать на любом процессоре, а не только Nehalem или более поздней версии.)

однако лучший способ использовать векторные инструкции для popcount обычно заключается в использовании переменной-shuffle для поиска таблицы для 4 бит за раз каждый байт параллельно. (4 бита индексируют таблицу 16 записей, хранящуюся в векторном регистре).

на процессорах Intel, инструкция 64bit popcnt оборудования может превзойти SSSE3 PSHUFB бит-параллельная реализация примерно в 2 раза, но только если ваш компилятор получает это только право. В противном случае SSE может выйти значительно вперед. Более новые версии компилятора знают о popcnt ложная зависимость проблема по Корпорация Intel.

ссылки:

https://graphics.stanford.edu / ~seander/bithacks.html

https://en.wikipedia.org/wiki/Hamming_weight

http://gurmeet.net/puzzles/fast-bit-counting-routines/

http://aggregate.ee.engr.uky.edu/MAGIC/#Population%20Count%20(Ones%20Count)


также рассмотрим встроенные функции компилятора.

на компиляторе GNU, например, вы можете просто использовать:

int __builtin_popcount (unsigned int x);
int __builtin_popcountll (unsigned long long x);

в худшем случае компилятор будет генерировать вызов функции. В лучшем случае компилятор выдаст инструкцию cpu для выполнения той же работы быстрее.

внутреннеприсущие компоненты GCC даже работают на нескольких платформах. Popcount станет мейнстримом в архитектуре x86, поэтому имеет смысл начать использовать встроенный сейчас. Другие модели имеют popcount в течение многих лет.


на x86 вы можете сообщить компилятору, что он может принять поддержку popcnt инструкция -mpopcnt или -msse4.2 также включить векторные инструкции, которые были добавлены в том же поколении. См.параметры GCC x86. -march=nehalem (или -march= любой процессор, который вы хотите, чтобы ваш код предполагал и настраивал), может быть хорошим выбором. Запуск результирующего двоичного файла на более старом процессоре приведет к незаконная-ошибка инструкции.

чтобы сделать двоичные файлы оптимизированными для машины, на которой вы их строите, используйте -march=native (С gcc, clang или ICC).

MSVC предоставляет встроенное для x86 popcnt - инструкции, но в отличие от gcc, он действительно является встроенным для аппаратной инструкции и требует аппаратной поддержки.


используя std::bitset<>::count() вместо встроенного

теоретически, любой компилятор, который знает, как popcount эффективно для целевого процессора должен предоставлять эту функциональность через ISO C++ std::bitset<>. На практике вам может быть лучше с Бит-Хак и / shift / ADD в некоторых случаях для некоторых целевых процессоров.

для целевых архитектур, где аппаратное popcount является дополнительным расширением (например, x86), не все компиляторы имеют std::bitset это использует его, когда доступно. Например, MSVC не может включить popcnt поддержка во время компиляции, и всегда использует таблица подстановки, даже /Ox /arch:AVX (что подразумевает SSE4.2, хотя технически существует отдельный бит функции для popcnt.)

но, по крайней мере, вы получаете что-то портативное, которое работает везде, и с gcc/clang с правильными целевыми параметрами вы получаете аппаратный popcount для архитектур, которые его поддерживают.

#include <bitset>
#include <limits>
#include <type_traits>

template<typename T>
//static inline  // static if you want to compile with -mpopcnt in one compilation unit but not others
typename std::enable_if<std::is_integral<T>::value,  unsigned >::type 
popcount(T x)
{
    static_assert(std::numeric_limits<T>::radix == 2, "non-binary type");

    // sizeof(x)*CHAR_BIT
    constexpr int bitwidth = std::numeric_limits<T>::digits + std::numeric_limits<T>::is_signed;
    // std::bitset constructor was only unsigned long before C++11.  Beware if porting to C++03
    static_assert(bitwidth <= std::numeric_limits<unsigned long long>::digits, "arg too wide for std::bitset() constructor");

    typedef typename std::make_unsigned<T>::type UT;        // probably not needed, bitset width chops after sign-extension

    std::bitset<bitwidth> bs( static_cast<UT>(x) );
    return bs.count();
}

посмотреть asm от gcc, clang, icc и MSVC в проводнике компилятора Godbolt.

x86-64 gcc -O3 -std=gnu++11 -mpopcnt выдает это:

unsigned test_short(short a) { return popcount(a); }
    movzx   eax, di      # note zero-extension, not sign-extension
    popcnt  rax, rax
    ret
unsigned test_int(int a) { return popcount(a); }
    mov     eax, edi
    popcnt  rax, rax
    ret
unsigned test_u64(unsigned long long a) { return popcount(a); }
    xor     eax, eax     # gcc avoids false dependencies for Intel CPUs
    popcnt  rax, rdi
    ret

PowerPC64 gcc -O3 -std=gnu++11 излучает (для int версия arg):

    rldicl 3,3,0,32     # zero-extend from 32 to 64-bit
    popcntd 3,3         # popcount
    blr

этот источник не является специфичным для x86 или GNU, но только хорошо компилируется для x86 с gcc/clang/icc.

также обратите внимание, что резервным вариантом gcc для архитектур без popcount с одной инструкцией является поиск таблицы "байт за раз". Это не замечательно!--65-->для ARM, например.


на мой взгляд, "лучшим" решением является то, которое может быть прочитано другим программистом (или оригинальным программистом два года спустя) без обильных комментариев. Вам может понадобиться самое быстрое или самое умное решение, которое некоторые уже предоставили, но я предпочитаю читаемость уму в любое время.

unsigned int bitCount (unsigned int value) {
    unsigned int count = 0;
    while (value > 0) {           // until all bits are zero
        if ((value & 1) == 1)     // check lower bit
            count++;
        value >>= 1;              // shift bits, removing lower bit
    }
    return count;
}

Если вы хотите больше скорости (и предполагая, что вы хорошо документируете ее, чтобы помочь своим преемникам), вы можете использовать поиск таблицы:

// Lookup table for fast calculation of bits set in 8-bit unsigned char.

static unsigned char oneBitsInUChar[] = {
//  0  1  2  3  4  5  6  7  8  9  A  B  C  D  E  F (<- n)
//  =====================================================
    0, 1, 1, 2, 1, 2, 2, 3, 1, 2, 2, 3, 2, 3, 3, 4, // 0n
    1, 2, 2, 3, 2, 3, 3, 4, 2, 3, 3, 4, 3, 4, 4, 5, // 1n
    : : :
    4, 5, 5, 6, 5, 6, 6, 7, 5, 6, 6, 7, 6, 7, 7, 8, // Fn
};

// Function for fast calculation of bits set in 16-bit unsigned short.

unsigned char oneBitsInUShort (unsigned short x) {
    return oneBitsInUChar [x >>    8]
         + oneBitsInUChar [x &  0xff];
}

// Function for fast calculation of bits set in 32-bit unsigned int.

unsigned char oneBitsInUInt (unsigned int x) {
    return oneBitsInUShort (x >>     16)
         + oneBitsInUShort (x &  0xffff);
}

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


от восторга хакера, р. 66, рис. 5-2

int pop(unsigned x)
{
    x = x - ((x >> 1) & 0x55555555);
    x = (x & 0x33333333) + ((x >> 2) & 0x33333333);
    x = (x + (x >> 4)) & 0x0F0F0F0F;
    x = x + (x >> 8);
    x = x + (x >> 16);
    return x & 0x0000003F;
}

выполняет в ~ 20-иш инструкциях (зависит от arch), без ветвления.

восторг хакера и восхитительно! Настоятельно рекомендуемый.


я думаю самый быстрый способ-без использования таблицы подстановки и popcount-это следующее. Он подсчитывает установленные биты всего за 12 операций.

int popcount(int v) {
    v = v - ((v >> 1) & 0x55555555);                // put count of each 2 bits into those 2 bits
    v = (v & 0x33333333) + ((v >> 2) & 0x33333333); // put count of each 4 bits into those 4 bits  
    return c = ((v + (v >> 4) & 0xF0F0F0F) * 0x1010101) >> 24;
}

это работает, потому что вы можете подсчитать общее количество установленных битов, разделив на две половины, подсчитав количество установленных битов в обеих половинах, а затем сложив их. Также знаю, как Divide and Conquer парадигмы. Давайте перейдем к деталям..

v = v - ((v >> 1) & 0x55555555); 

число битов в двух битах может быть 0b00, 0b01 или 0b10. Давайте попробуем разобраться с этим на 2 битах..

 ---------------------------------------------
 |   v    |   (v >> 1) & 0b0101   |  v - x   |
 ---------------------------------------------
   0b00           0b00               0b00   
   0b01           0b00               0b01     
   0b10           0b01               0b01
   0b11           0b01               0b10

это то, что требовалось: последний столбец показывает количество бит в бит каждые две пары. Если двухразрядное число >= 2 (0b10) затем and производит 0b01, иначе она производит 0b00.

v = (v & 0x33333333) + ((v >> 2) & 0x33333333); 

это утверждение должно быть легко понять. После первой операции у нас есть количество установленных битов в каждых двух битах, теперь мы суммируем это количество в каждых 4 битах.

v & 0b00110011         //masks out even two bits
(v >> 2) & 0b00110011  // masks out odd two bits

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

c = ((v + (v >> 4) & 0xF0F0F0F) * 0x1010101) >> 24;

давайте разберем его дальше...

v + (v >> 4)

это похоже на второе утверждение; вместо этого мы подсчитываем установленные биты в группах по 4. Мы знаем-из-за наших предыдущих операций-что каждый кусочек имеет количество установленных битов. Давайте рассмотрим пример. Предположим, у нас есть байт 0b01000010. Это означает, что первый укус имеет набор 4bits и второй имеет свой набор 2bits. Теперь мы сложим эти кусочки вместе.

0b01000010 + 0b01000000

это дает нам количество установленных битов в байте, в первом кусочке 0b01100010 и поэтому мы маскируем последние четыре байта всех байтов в номере (отбрасывая их).

0b01100010 & 0xF0 = 0b01100000

теперь каждый байт имеет количество установленных битов в нем. Мы должны сложить их все вместе. Фокус в том, чтобы умножить результат на 0b10101010, который имеет интересное свойство. Если наше число имеет четыре байта, A B C D, это приведет к новому номеру с этими байтами A+B+C+D B+C+D C+D D. 4-байтовое число может иметь максимум 32 бит, которые могут быть представлены как 0b00100000.

все, что нам сейчас нужно-это первый байт, который имеет сумму всех битов в байты, и мы получаем его >> 24. Этот алгоритм был разработан для 32 bit слова, но могут быть легко изменены для 64 bit слова.


Мне стало скучно, и я приурочил миллиард итераций трех подходов. Компилятор-gcc-O3. CPU-это все, что они помещают в 1st gen Macbook Pro.

самый быстрый следующий, на 3,7 секунды:

static unsigned char wordbits[65536] = { bitcounts of ints between 0 and 65535 };
static int popcount( unsigned int i )
{
    return( wordbits[i&0xFFFF] + wordbits[i>>16] );
}

второе место занимает один и тот же код, но глядя на 4 байта вместо 2 halfwords. Это заняло около 5,5 секунд.

третье место занимает бит-крутящийся подход "боковое дополнение", который занял 8,6 секунды.

четвертое место занимает ССАГПЗ __строение_popcount(), на позорное 11 секунд.

подсчет один бит за раз подход был waaaay медленнее, и мне надоело ждать его завершения.

поэтому, если вы заботитесь о производительности прежде всего, используйте первый подход. Если вам не все равно, но недостаточно потратить на это 64 КБ ОЗУ, используйте второй подход. В противном случае используйте читаемый (но медленный) одноразовый подход.

трудно думать о ситуации, когда вы хотели бы использовать немного изворотливый подход.

Edit: похожие результаты здесь.


Если вы используете Java, встроенный метод Integer.bitCount сделаю это.


unsigned int count_bit(unsigned int x)
{
  x = (x & 0x55555555) + ((x >> 1) & 0x55555555);
  x = (x & 0x33333333) + ((x >> 2) & 0x33333333);
  x = (x & 0x0F0F0F0F) + ((x >> 4) & 0x0F0F0F0F);
  x = (x & 0x00FF00FF) + ((x >> 8) & 0x00FF00FF);
  x = (x & 0x0000FFFF) + ((x >> 16)& 0x0000FFFF);
  return x;
}

позвольте мне объяснить этот алгоритм.

этот алгоритм основан на алгоритме Divide и Conquer. Предположим, что существует 8-битное целое число 213 (11010101 в двоичном формате), алгоритм работает следующим образом(каждый раз объединяют два соседних блока):

+-------------------------------+
| 1 | 1 | 0 | 1 | 0 | 1 | 0 | 1 |  <- x
|  1 0  |  0 1  |  0 1  |  0 1  |  <- first time merge
|    0 0 1 1    |    0 0 1 0    |  <- second time merge
|        0 0 0 0 0 1 0 1        |  <- third time ( answer = 00000101 = 5)
+-------------------------------+

Это один из тех вопросов, где он помогает узнать свой микро-архитектуры. Я только что приурочил два варианта под gcc 4.3.3, скомпилированные с-O3 с использованием C++ inlines для устранения накладных расходов на вызов функции, один миллиард итераций, сохраняя текущую сумму всех подсчетов, чтобы убедиться, что компилятор не удаляет ничего важного, используя RDTSC для синхронизации (точный такт).

inline int pop2(unsigned x, unsigned y)
{
    x = x - ((x >> 1) & 0x55555555);
    y = y - ((y >> 1) & 0x55555555);
    x = (x & 0x33333333) + ((x >> 2) & 0x33333333);
    y = (y & 0x33333333) + ((y >> 2) & 0x33333333);
    x = (x + (x >> 4)) & 0x0F0F0F0F;
    y = (y + (y >> 4)) & 0x0F0F0F0F;
    x = x + (x >> 8);
    y = y + (y >> 8);
    x = x + (x >> 16);
    y = y + (y >> 16);
    return (x+y) & 0x000000FF;
}

восторг немодифицированного хакера занял 12,2 гигацикла. Моя параллельная версия (считая в два раза больше много битов) работает в 13.0 гигациклах. 10.5 s всего прошло для обоих вместе на 2.4 GHz Core Duo. 25 gigacycles = чуть больше 10 секунд на это тактовая частота, так что я уверен, мои расчеты правы.

это связано с цепочками зависимостей инструкций, которые очень плохи для этого алгоритма. Я мог бы почти удвоить скорость снова, используя пару 64-битных регистров. На самом деле, если бы я был умным и добавил x+y немного раньше, я мог бы сбрить некоторые смены. 64-разрядная версия с некоторые небольшие настройки выйдут примерно равными, но снова посчитайте в два раза больше бит.

С 128-битными регистрами SIMD, еще один фактор из двух, и наборы инструкций SSE часто имеют умные короткие сокращения.

нет причин для того, чтобы код был особенно прозрачным. Интерфейс прост, алгоритм можно ссылаться на он-лайн во многих местах, и он поддается всестороннему модульному тесту. Программист, который наткнется на него, может даже чему-то научиться. Эти битовые операции чрезвычайно естественны на машинном уровне.

хорошо, я решил скамью 64-битную версию. Для этого одного sizeof (unsigned long) == 8

inline int pop2(unsigned long x, unsigned long y)
{
    x = x - ((x >> 1) & 0x5555555555555555);
    y = y - ((y >> 1) & 0x5555555555555555);
    x = (x & 0x3333333333333333) + ((x >> 2) & 0x3333333333333333);
    y = (y & 0x3333333333333333) + ((y >> 2) & 0x3333333333333333);
    x = (x + (x >> 4)) & 0x0F0F0F0F0F0F0F0F;
    y = (y + (y >> 4)) & 0x0F0F0F0F0F0F0F0F;
    x = x + y; 
    x = x + (x >> 8);
    x = x + (x >> 16);
    x = x + (x >> 32); 
    return x & 0xFF;
}

это выглядит правильно (я не тестирую тщательно, хотя). Теперь пришло время выйти на 10.70 gigacycles / 14.1 gigacycles. Это более позднее число суммировало 128 миллиардов битов и соответствует 5.9 s, прошедшим на этой машине. Непараллельная версия ускоряется немного, потому что я бегу 64-разрядный режим и он любит 64-разрядные регистры немного лучше, чем 32-разрядные регистры.

давайте посмотрим, есть ли здесь немного больше OOO pipelining. Это было немного сложнее,поэтому я немного протестировал. Каждый член сам по себе составляет 64, все вместе взятые-256.

inline int pop4(unsigned long x, unsigned long y, 
                unsigned long u, unsigned long v)
{
  enum { m1 = 0x5555555555555555, 
         m2 = 0x3333333333333333, 
         m3 = 0x0F0F0F0F0F0F0F0F, 
         m4 = 0x000000FF000000FF };

    x = x - ((x >> 1) & m1);
    y = y - ((y >> 1) & m1);
    u = u - ((u >> 1) & m1);
    v = v - ((v >> 1) & m1);
    x = (x & m2) + ((x >> 2) & m2);
    y = (y & m2) + ((y >> 2) & m2);
    u = (u & m2) + ((u >> 2) & m2);
    v = (v & m2) + ((v >> 2) & m2);
    x = x + y; 
    u = u + v; 
    x = (x & m3) + ((x >> 4) & m3);
    u = (u & m3) + ((u >> 4) & m3);
    x = x + u; 
    x = x + (x >> 8);
    x = x + (x >> 16);
    x = x & m4; 
    x = x + (x >> 32);
    return x & 0x000001FF;
}

Я был взволнован на мгновение, но оказалось, что gcc играет встроенные трюки с-O3, хотя я не использую ключевое слово inline в некоторых тестах. Когда я позволяю gcc играть трюки, миллиард звонков pop4() принимает gigacycles 12.56, но я решила, что это был складной аргументы как константные выражения. Более реалистичное число, по-видимому, составляет 19,6 gc для еще 30% ускорения. Мой тестовый цикл теперь выглядит так, убедившись, что каждый аргумент достаточно разные, чтобы остановить ССЗ играет.

   hitime b4 = rdtsc(); 
   for (unsigned long i = 10L * 1000*1000*1000; i < 11L * 1000*1000*1000; ++i) 
      sum += pop4 (i,  i^1, ~i, i|1); 
   hitime e4 = rdtsc(); 

256 миллиардов бит, суммированных за 8.17 секунд. Работает на 1.02 С для 32 миллионов бит в качестве эталона в 16-разрядной таблицы подстановки. Не могу сравнивать напрямую, потому что другая скамейка не дайте тактовую частоту, но похоже, что я ударил сопли из 64KB table edition, что является трагическим использованием кэша L1 в первую очередь.

Update: решил сделать очевидное и создать pop6 (), добавив еще четыре дублированные строки. Вышел на 22,8 Гц, 384 млрд бит, суммированных за 9,5 с. Таким образом, есть еще 20% сейчас на 800ms для 32 миллиардов бит.


почему бы итеративно не разделить на 2?

count = 0
while n > 0
  if (n % 2) == 1
    count += 1
  n /= 2  

Я согласен, что это не самый быстрый, но "лучший" является несколько двусмысленным. Я бы сказал, что "лучший" должен иметь элемент ясности


для счастливой среды между 232 таблица поиска и итерация через каждый бит по отдельности:

int bitcount(unsigned int num){
    int count = 0;
    static int nibblebits[] =
        {0, 1, 1, 2, 1, 2, 2, 3, 1, 2, 2, 3, 2, 3, 3, 4};
    for(; num != 0; num >>= 4)
        count += nibblebits[num & 0x0f];
    return count;
}

от http://ctips.pbwiki.com/CountBits


восхищение хакера бит-twiddling становится намного яснее, когда вы пишете битовые шаблоны.

unsigned int bitCount(unsigned int x)
{
  x = (((x >> 1) & 0b01010101010101010101010101010101)
       + x       & 0b01010101010101010101010101010101);
  x = (((x >> 2) & 0b00110011001100110011001100110011)
       + x       & 0b00110011001100110011001100110011); 
  x = (((x >> 4) & 0b00001111000011110000111100001111)
       + x       & 0b00001111000011110000111100001111); 
  x = (((x >> 8) & 0b00000000111111110000000011111111)
       + x       & 0b00000000111111110000000011111111); 
  x = (((x >> 16)& 0b00000000000000001111111111111111)
       + x       & 0b00000000000000001111111111111111); 
  return x;
}

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


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

unsigned int f(unsigned int x)
{
    switch (x) {
        case 0:
            return 0;
        case 1:
            return 1;
        case 2:
            return 1;
        case 3:
            return 2;
        default:
            return f(x/4) + f(x%4);
    }
}

Это можно сделать в O(k), где k - это количество бит.

int NumberOfSetBits(int n)
{
    int count = 0;

    while (n){
        ++ count;
        n = (n - 1) & n;
    }

    return count;
}

функция, которую вы ищете, часто называется "боковой суммой" или "количеством населения" двоичного числа. Кнут обсуждает это в pre-Fascicle 1A, pp11-12(хотя была краткая ссылка в Томе 2, 4.6.3-(7).)

на locus classicus это статья Питера Вегнера "техника подсчета единиц в двоичном компьютере", из связь ACM, Том 3 (1960) номер 5, Страница 322. Он дает два разных алгоритмы там, один оптимизированный для чисел, как ожидается, будет" разреженным " (т. е. иметь небольшое количество единиц) и один для противоположного случая.


несколько открытых вопросов:-

  1. если число отрицательное то?
  2. если число равно 1024, то метод "итеративно разделить на 2" будет повторяться 10 раз.

мы можем изменить algo для поддержки отрицательного числа следующим образом: -

count = 0
while n != 0
if ((n % 2) == 1 || (n % 2) == -1
    count += 1
  n /= 2  
return count

теперь, чтобы преодолеть вторую проблему, мы можем написать algo как: -

int bit_count(int num)
{
    int count=0;
    while(num)
    {
        num=(num)&(num-1);
        count++;
    }
    return count;
}

для полной справки см. :

http://goursaha.freeoda.com/Miscellaneous/IntegerBitCount.html


  private int get_bits_set(int v)
    {
      int c; // c accumulates the total bits set in v
        for (c = 0; v>0; c++)
        {
            v &= v - 1; // clear the least significant bit set
        }
        return c;
    }

Я думаю Брайана Кернигана метод также будет полезен... Он проходит столько итераций, сколько есть заданных битов. Поэтому, если у нас есть 32-битное слово только с высоким битом, то оно будет проходить только один раз через цикл.

int countSetBits(unsigned int n) { 
    unsigned int n; // count the number of bits set in n
    unsigned int c; // c accumulates the total bits set in n
    for (c=0;n>0;n=n&(n-1)) c++; 
    return c; 
}

опубликовано в 1988, The C Programming Language 2nd Ed. Керниган и Деннис Ричи) упоминает об этом в упражнении 2-9. 19 апреля 2006 года Дон кнут указал мне, что этот метод "был впервые опубликован Питер Вегнер в CACM 3 (1960), 322. (Также независимо открыт Дерриком Лемером и опубликован в 1964 году в книге под редакцией Бекенбаха.)"


Я использую приведенный ниже код, который более интуитивно понятен.

int countSetBits(int n) {
    return !n ? 0 : 1 + countSetBits(n & (n-1));
}

логика: N & (n-1) сбрасывает последний бит набора n.

P. S: Я знаю, что это не решение O(1), хотя и интересное решение.


что вы подразумеваете под "лучшим алгоритмом"? Короткозамкнутый код или код постился? Ваш код выглядит очень элегантно и имеет постоянное время выполнения. Код очень короткий.

но если скорость является основным фактором, а не размер кода, то я думаю, что может быть быстрее:

       static final int[] BIT_COUNT = { 0, 1, 1, ... 256 values with a bitsize of a byte ... };
        static int bitCountOfByte( int value ){
            return BIT_COUNT[ value & 0xFF ];
        }

        static int bitCountOfInt( int value ){
            return bitCountOfByte( value ) 
                 + bitCountOfByte( value >> 8 ) 
                 + bitCountOfByte( value >> 16 ) 
                 + bitCountOfByte( value >> 24 );
        }

Я думаю, что это не будет быстрее для 64-битного значения, но 32-битное значение может быть быстрее.


Я написал быстрый макрос bitcount для машин RISC примерно в 1990 году. Он не использует расширенную арифметику (умножение, деление, %), выборки памяти (слишком медленные), ветви (слишком медленные), но он предполагает, что процессор имеет 32-битный сдвигатель барреля (другими словами, > > 1 и >> 32 занимают одинаковое количество циклов.) Предполагается, что малые константы (например, 6, 12, 24) ничего не стоят для загрузки в регистры или хранятся во временных хранилищах и повторно используются снова и снова.

С эти предположения, он подсчитывает 32 бита в около 16 циклах / инструкциях на большинстве машин RISC. Обратите внимание, что 15 инструкций/циклов близки к нижней границе количества циклов или инструкций, потому что, похоже, требуется не менее 3 инструкций (маска, сдвиг, оператор), чтобы сократить количество добавлений наполовину, поэтому log_2(32) = 5, 5 x 3 = 15 инструкций является квази-нижней границей.

#define BitCount(X,Y)           \
                Y = X - ((X >> 1) & 033333333333) - ((X >> 2) & 011111111111); \
                Y = ((Y + (Y >> 3)) & 030707070707); \
                Y =  (Y + (Y >> 6)); \
                Y = (Y + (Y >> 12) + (Y >> 24)) & 077;

вот секрет первого и самого сложного шага:

input output
AB    CD             Note
00    00             = AB
01    01             = AB
10    01             = AB - (A >> 1) & 0x1
11    10             = AB - (A >> 1) & 0x1

Так если я возьму 1-й столбец (A) выше, сдвиньте его вправо на 1 бит и вычитайте его из AB, я получаю выход (CD). Расширение до 3 бит аналогично; вы можете проверить его с помощью 8-строчной логической таблицы, такой как моя выше, если хотите.

  • Дон Гиллис

Если вы используете C++, другой вариант-использовать метапрограммирование шаблонов:

// recursive template to sum bits in an int
template <int BITS>
int countBits(int val) {
        // return the least significant bit plus the result of calling ourselves with
        // .. the shifted value
        return (val & 0x1) + countBits<BITS-1>(val >> 1);
}

// template specialisation to terminate the recursion when there's only one bit left
template<>
int countBits<1>(int val) {
        return val & 0x1;
}

загрузка может быть:

// to count bits in a byte/char (this returns 8)
countBits<8>( 255 )

// another byte (this returns 7)
countBits<8>( 254 )

// counting bits in a word/short (this returns 1)
countBits<16>( 256 )

вы можете, конечно, еще больше расширить этот шаблон, чтобы использовать разные типы (даже автоопределение размера бита), но я сохранил его простым для ясности.

edit: забыл упомянуть, что это хорошо, потому что это должны работа в любом компиляторе C++, и он в основном просто разворачивает ваш цикл для вас, если постоянное значение используется для bit count (другими словами, Я уверен, что это самый быстрый способ вы найдете)


мне особенно нравится этот пример из файла fortune:

#define BITCOUNT(x)    (((BX_(x)+(BX_(x)>>4)) & 0x0F0F0F0F) % 255)
#define BX_(x)         ((x) - (((x)>>1)&0x77777777)
                             - (((x)>>2)&0x33333333)
                             - (((x)>>3)&0x11111111))

мне больше нравится, потому что это так красиво!


Java JDK1.5

целое число.bitCount (n);

где n-число, 1 которого необходимо сосчитать.

проверяем,
Integer.highestOneBit(n);
Integer.lowestOneBit(n);
Integer.numberOfLeadingZeros(n);
Integer.numberOfTrailingZeros(n);

//Beginning with the value 1, rotate left 16 times
     n = 1;
         for (int i = 0; i < 16; i++) {
            n = Integer.rotateLeft(n, 1);
            System.out.println(n);
         }

Я нашел реализацию подсчета битов в массиве с использованием инструкции SIMD (SSSE3 и AVX2). Он имеет в 2-2.Производительность в 5 раз лучше, чем при использовании встроенной функции __popcnt64.

версия SSSE3:

#include <smmintrin.h>
#include <stdint.h>

const __m128i Z = _mm_set1_epi8(0x0);
const __m128i F = _mm_set1_epi8(0xF);
//Vector with pre-calculated bit count:
const __m128i T = _mm_setr_epi8(0, 1, 1, 2, 1, 2, 2, 3, 1, 2, 2, 3, 2, 3, 3, 4);

uint64_t BitCount(const uint8_t * src, size_t size)
{
    __m128i _sum =  _mm128_setzero_si128();
    for (size_t i = 0; i < size; i += 16)
    {
        //load 16-byte vector
        __m128i _src = _mm_loadu_si128((__m128i*)(src + i));
        //get low 4 bit for every byte in vector
        __m128i lo = _mm_and_si128(_src, F);
        //sum precalculated value from T
        _sum = _mm_add_epi64(_sum, _mm_sad_epu8(Z, _mm_shuffle_epi8(T, lo)));
        //get high 4 bit for every byte in vector
        __m128i hi = _mm_and_si128(_mm_srli_epi16(_src, 4), F);
        //sum precalculated value from T
        _sum = _mm_add_epi64(_sum, _mm_sad_epu8(Z, _mm_shuffle_epi8(T, hi)));
    }
    uint64_t sum[2];
    _mm_storeu_si128((__m128i*)sum, _sum);
    return sum[0] + sum[1];
}

поддержкой AVX2 версия:

#include <immintrin.h>
#include <stdint.h>

const __m256i Z = _mm256_set1_epi8(0x0);
const __m256i F = _mm256_set1_epi8(0xF);
//Vector with pre-calculated bit count:
const __m256i T = _mm256_setr_epi8(0, 1, 1, 2, 1, 2, 2, 3, 1, 2, 2, 3, 2, 3, 3, 4, 
                                   0, 1, 1, 2, 1, 2, 2, 3, 1, 2, 2, 3, 2, 3, 3, 4);

uint64_t BitCount(const uint8_t * src, size_t size)
{
    __m256i _sum =  _mm256_setzero_si256();
    for (size_t i = 0; i < size; i += 32)
    {
        //load 32-byte vector
        __m256i _src = _mm256_loadu_si256((__m256i*)(src + i));
        //get low 4 bit for every byte in vector
        __m256i lo = _mm256_and_si256(_src, F);
        //sum precalculated value from T
        _sum = _mm256_add_epi64(_sum, _mm256_sad_epu8(Z, _mm256_shuffle_epi8(T, lo)));
        //get high 4 bit for every byte in vector
        __m256i hi = _mm256_and_si256(_mm256_srli_epi16(_src, 4), F);
        //sum precalculated value from T
        _sum = _mm256_add_epi64(_sum, _mm256_sad_epu8(Z, _mm256_shuffle_epi8(T, hi)));
    }
    uint64_t sum[4];
    _mm256_storeu_si256((__m256i*)sum, _sum);
    return sum[0] + sum[1] + sum[2] + sum[3];
}

Я всегда использую это в конкурентном программировании, и это легко написать и эффективно:

#include <bits/stdc++.h>

using namespace std;

int countOnes(int n) {
    bitset<32> b(n);
    return b.count();
}

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

Бит Сложа Хаки

Я предлагаю этот:

подсчет битов, установленных в 14, 24 или 32-разрядных словах с использованием 64-разрядных инструкций

unsigned int v; // count the number of bits set in v
unsigned int c; // c accumulates the total bits set in v

// option 1, for at most 14-bit values in v:
c = (v * 0x200040008001ULL & 0x111111111111111ULL) % 0xf;

// option 2, for at most 24-bit values in v:
c =  ((v & 0xfff) * 0x1001001001001ULL & 0x84210842108421ULL) % 0x1f;
c += (((v & 0xfff000) >> 12) * 0x1001001001001ULL & 0x84210842108421ULL) 
     % 0x1f;

// option 3, for at most 32-bit values in v:
c =  ((v & 0xfff) * 0x1001001001001ULL & 0x84210842108421ULL) % 0x1f;
c += (((v & 0xfff000) >> 12) * 0x1001001001001ULL & 0x84210842108421ULL) % 
     0x1f;
c += ((v >> 24) * 0x1001001001001ULL & 0x84210842108421ULL) % 0x1f;

этот метод требует 64-разрядного процессора с быстрым разделением по модулю, чтобы быть эффективным. Первый вариант занимает всего 3 операции; Второй опция 10; и третий вариант за 15.


вот портативный модуль (ANSI-C), который может проверять каждый из ваших алгоритмов на любой архитектуре.

ваш процессор имеет 9 бит байт? Нет проблем : -) на данный момент он реализует 2 алгоритма, алгоритм K&R и таблицу поиска байтов. Таблица поиска в среднем в 3 раза быстрее, чем алгоритм K&R. Если кто-то может придумать способ сделать алгоритм "Hacker's Delight" портативным, не стесняйтесь добавлять его.

#ifndef _BITCOUNT_H_
#define _BITCOUNT_H_

/* Return the Hamming Wieght of val, i.e. the number of 'on' bits. */
int bitcount( unsigned int );

/* List of available bitcount algorithms.  
 * onTheFly:    Calculate the bitcount on demand.
 *
 * lookupTalbe: Uses a small lookup table to determine the bitcount.  This
 * method is on average 3 times as fast as onTheFly, but incurs a small
 * upfront cost to initialize the lookup table on the first call.
 *
 * strategyCount is just a placeholder. 
 */
enum strategy { onTheFly, lookupTable, strategyCount };

/* String represenations of the algorithm names */
extern const char *strategyNames[];

/* Choose which bitcount algorithm to use. */
void setStrategy( enum strategy );

#endif

.

#include <limits.h>

#include "bitcount.h"

/* The number of entries needed in the table is equal to the number of unique
 * values a char can represent which is always UCHAR_MAX + 1*/
static unsigned char _bitCountTable[UCHAR_MAX + 1];
static unsigned int _lookupTableInitialized = 0;

static int _defaultBitCount( unsigned int val ) {
    int count;

    /* Starting with:
     * 1100 - 1 == 1011,  1100 & 1011 == 1000
     * 1000 - 1 == 0111,  1000 & 0111 == 0000
     */
    for ( count = 0; val; ++count )
        val &= val - 1;

    return count;
}

/* Looks up each byte of the integer in a lookup table.
 *
 * The first time the function is called it initializes the lookup table.
 */
static int _tableBitCount( unsigned int val ) {
    int bCount = 0;

    if ( !_lookupTableInitialized ) {
        unsigned int i;
        for ( i = 0; i != UCHAR_MAX + 1; ++i )
            _bitCountTable[i] =
                ( unsigned char )_defaultBitCount( i );

        _lookupTableInitialized = 1;
    }

    for ( ; val; val >>= CHAR_BIT )
        bCount += _bitCountTable[val & UCHAR_MAX];

    return bCount;
}

static int ( *_bitcount ) ( unsigned int ) = _defaultBitCount;

const char *strategyNames[] = { "onTheFly", "lookupTable" };

void setStrategy( enum strategy s ) {
    switch ( s ) {
    case onTheFly:
        _bitcount = _defaultBitCount;
        break;
    case lookupTable:
        _bitcount = _tableBitCount;
        break;
    case strategyCount:
        break;
    }
}

/* Just a forwarding function which will call whichever version of the
 * algorithm has been selected by the client 
 */
int bitcount( unsigned int val ) {
    return _bitcount( val );
}

#ifdef _BITCOUNT_EXE_

#include <stdio.h>
#include <stdlib.h>
#include <time.h>

/* Use the same sequence of pseudo random numbers to benmark each Hamming
 * Weight algorithm.
 */
void benchmark( int reps ) {
    clock_t start, stop;
    int i, j;
    static const int iterations = 1000000;

    for ( j = 0; j != strategyCount; ++j ) {
        setStrategy( j );

        srand( 257 );

        start = clock(  );

        for ( i = 0; i != reps * iterations; ++i )
            bitcount( rand(  ) );

        stop = clock(  );

        printf
            ( "\n\t%d psudoe-random integers using %s: %f seconds\n\n",
              reps * iterations, strategyNames[j],
              ( double )( stop - start ) / CLOCKS_PER_SEC );
    }
}

int main( void ) {
    int option;

    while ( 1 ) {
        printf( "Menu Options\n"
            "\t1.\tPrint the Hamming Weight of an Integer\n"
            "\t2.\tBenchmark Hamming Weight implementations\n"
            "\t3.\tExit ( or cntl-d )\n\n\t" );

        if ( scanf( "%d", &option ) == EOF )
            break;

        switch ( option ) {
        case 1:
            printf( "Please enter the integer: " );
            if ( scanf( "%d", &option ) != EOF )
                printf
                    ( "The Hamming Weight of %d ( 0x%X ) is %d\n\n",
                      option, option, bitcount( option ) );
            break;
        case 2:
            printf
                ( "Please select number of reps ( in millions ): " );
            if ( scanf( "%d", &option ) != EOF )
                benchmark( option );
            break;
        case 3:
            goto EXIT;
            break;
        default:
            printf( "Invalid option\n" );
        }

    }

 EXIT:
    printf( "\n" );

    return 0;
}

#endif

32-битные или нет ? Я просто пришел с этим методом на Java после чтения"взлом кодирования интервью" 4-е издание exercice 5.5 (chap 5: Bit Manipulation). Если наименьший значимый бит равен 1 increment count, затем вправо-сдвиг на целое число.

public static int bitCount( int n){
    int count = 0;
    for (int i=n; i!=0; i = i >> 1){
        count += i & 1;
    }
    return count;
}

Я думаю, что это более интуитивно, чем решения с константой 0x33333333, независимо от того, насколько они быстры. Это зависит от вашего определения "лучшего алгоритма" .


быстрое решение C# с использованием предварительно вычисленной таблицы битовых отсчетов байтов с ветвлением по размеру ввода.

public static class BitCount
{
    public static uint GetSetBitsCount(uint n)
    {
        var counts = BYTE_BIT_COUNTS;
        return n <= 0xff ? counts[n]
             : n <= 0xffff ? counts[n & 0xff] + counts[n >> 8]
             : n <= 0xffffff ? counts[n & 0xff] + counts[(n >> 8) & 0xff] + counts[(n >> 16) & 0xff]
             : counts[n & 0xff] + counts[(n >> 8) & 0xff] + counts[(n >> 16) & 0xff] + counts[(n >> 24) & 0xff];
    }

    public static readonly uint[] BYTE_BIT_COUNTS = 
    {
        0, 1, 1, 2, 1, 2, 2, 3, 1, 2, 2, 3, 2, 3, 3, 4,
        1, 2, 2, 3, 2, 3, 3, 4, 2, 3, 3, 4, 3, 4, 4, 5,
        1, 2, 2, 3, 2, 3, 3, 4, 2, 3, 3, 4, 3, 4, 4, 5,
        2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6,
        1, 2, 2, 3, 2, 3, 3, 4, 2, 3, 3, 4, 3, 4, 4, 5,
        2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6,
        2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6,
        3, 4, 4, 5, 4, 5, 5, 6, 4, 5, 5, 6, 5, 6, 6, 7,
        1, 2, 2, 3, 2, 3, 3, 4, 2, 3, 3, 4, 3, 4, 4, 5,
        2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6,
        2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6,
        3, 4, 4, 5, 4, 5, 5, 6, 4, 5, 5, 6, 5, 6, 6, 7,
        2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6,
        3, 4, 4, 5, 4, 5, 5, 6, 4, 5, 5, 6, 5, 6, 6, 7,
        3, 4, 4, 5, 4, 5, 5, 6, 4, 5, 5, 6, 5, 6, 6, 7,
        4, 5, 5, 6, 5, 6, 6, 7, 5, 6, 6, 7, 6, 7, 7, 8
    };
}