webentwicklung-frage-antwort-db.com.de

Wie zählt man die Anzahl der gesetzten Bits in einer 32-Bit-Ganzzahl?

8 Bits, die die Zahl 7 darstellen, sehen folgendermaßen aus:

00000111

Es sind drei Bits gesetzt. 

Was sind Algorithmen, um die Anzahl der gesetzten Bits in einer 32-Bit-Ganzzahl zu bestimmen?

795
Matt Howells

Dies ist bekannt als ' Hamming Weight ', 'Popcount' oder 'Seitwärtsaddition'.

Der 'beste' Algorithmus hängt wirklich davon ab, auf welcher CPU Sie sich befinden und wie Ihr Nutzungsverhalten ist.

Einige CPUs verfügen über eine einzige integrierte Anweisung, andere dagegen über parallele Anweisungen, die auf Bitvektoren wirken. Die parallelen Anweisungen (wie popcnt von x86, auf CPUs, auf denen sie unterstützt werden) werden fast sicher am schnellsten sein. Bei einigen anderen Architekturen kann ein langsamer Befehl mit einer Mikrocodierschleife implementiert werden, die ein Bit pro Zyklus testet (citation required).

Eine vorbefüllte Tabellensuchmethode kann sehr schnell sein, wenn Ihre CPU über einen großen Cache verfügt und/oder Sie viele dieser Anweisungen in einer engen Schleife ausführen. Es kann jedoch unter den Kosten eines "Cache-Fehlschlags" leiden, bei dem die CPU einen Teil der Tabelle aus dem Hauptspeicher holen muss.

Wenn Sie wissen, dass Ihre Bytes meistens 0 oder 1 sind, gibt es sehr effiziente Algorithmen für diese Szenarien.

Ich glaube, ein sehr guter Universalalgorithmus ist der folgende, als "paralleler" oder "SWAR-Algorithmus mit variabler Genauigkeit" bekannt. Ich habe dies in einer C-ähnlichen Pseudo-Sprache ausgedrückt. Möglicherweise müssen Sie sie anpassen, um für eine bestimmte Sprache zu funktionieren (z. B. bei Verwendung von uint32_t für C++ und >>> in 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;
}

Dies hat das beste Worst-Case-Verhalten eines der besprochenen Algorithmen, so dass alle Verwendungsmuster und -werte, die Sie darauf werfen, effizient behandelt werden.


Dieser bitweise SWAR-Algorithmus könnte parallelisiert werden, um in mehreren Vektorelementen auf einmal statt in einem einzelnen Ganzzahlregister ausgeführt zu werden, um die CPU mit SIMD zu beschleunigen, aber keinen verwendbaren Popcount-Befehl. (Beispiel: x86-64-Code, der auf einer CPU ausgeführt werden muss, nicht nur in Nehalem oder höher.)

Die beste Methode zur Verwendung von Vektoranweisungen für popcount ist jedoch normalerweise die Verwendung einer Variablen-Shuffle, um eine Tabellensuche für jeweils 4 Bits von jedem Byte parallel durchzuführen. (Die 4 Bits indizieren eine Tabelle mit 16 Einträgen, die in einem Vektorregister gespeichert ist).

Auf Intel-CPUs kann der 64-Bit-Popcnt-Befehl eine SSSE3 PSHUFB-bitparallele Implementierung um einen Faktor von 2 übertreffen, aber nur wenn Ihr Compiler es richtig macht . Andernfalls kann SSE deutlich voraus sein. Neuere Compilerversionen kennen das popcnt false AbhängigkeitsProblem bei Intel .

Verweise:

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)

800
Matt Howells

Beachten Sie auch die integrierten Funktionen Ihrer Compiler.

Auf dem Compiler GNU können Sie beispielsweise Folgendes verwenden:

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

Im schlimmsten Fall generiert der Compiler eine Funktion. Im besten Fall gibt der Compiler eine CPU-Anweisung aus, um den gleichen Job schneller auszuführen.

Die GCC-Intrinsics funktionieren sogar auf mehreren Plattformen. Popcount wird in der x86-Architektur zum Mainstream, daher ist es sinnvoll, das Intrinsic jetzt zu verwenden. Andere Architekturen haben seit Jahren den Popcount.


Auf x86 können Sie dem Compiler mitteilen, dass er die Unterstützung für popcnt-Anweisung mit -mpopcnt oder -msse4.2 annehmen kann, um auch die Vektoranweisungen zu aktivieren, die in derselben Generation hinzugefügt wurden. Siehe GCC x86-Optionen . -march=nehalem (oder -march= für welche CPU auch immer Sie Ihren Code annehmen und einstellen möchten) könnte eine gute Wahl sein. Wenn Sie die resultierende Binärdatei auf einer älteren CPU ausführen, führt dies zu einem Fehler durch ungültige Anweisungen.

Verwenden Sie -march=native (mit gcc, clang oder ICC), um Binaries für die Maschine zu optimieren, auf der Sie sie erstellen.

MSVC bietet eine intrinsic für die x86 popcnt-Anweisung , aber im Gegensatz zu gcc ist dies wirklich eine intrinsische für die Hardwareanweisung und erfordert Hardwareunterstützung.


Verwenden von std::bitset<>::count() anstelle eines integrierten

Theoretisch sollte jeder Compiler, der weiß, wie er effizient für die Ziel-CPU popcount ist, diese Funktionalität durch ISO C++ std::bitset<> verfügbar machen. In der Praxis können Sie mit Bit-HACK AND/shift/ADD in manchen Fällen bei einigen Ziel-CPUs besser aufgehoben sein.

Bei Zielarchitekturen, bei denen Hardware-Popcount eine optionale Erweiterung ist (wie x86), verfügen nicht alle Compiler über einen std::bitset, der diese Funktion nutzt, wenn sie verfügbar ist. Beispielsweise hat MSVC keine Möglichkeit, die Unterstützung von popcnt zur Kompilierzeit zu aktivieren, und verwendet immer eine Tabellensuche , selbst mit /Ox /Arch:AVX (was SSE4.2 impliziert, obwohl technisch gesehen ein separates Funktionsbit für popcnt vorhanden ist).

Zumindest erhalten Sie etwas tragbares, das überall funktioniert. Mit gcc/clang und den richtigen Zieloptionen erhalten Sie Hardware-Popcount-Werte für Architekturen, die dies unterstützen.

#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();
}

Siehe asm von gcc, clang, icc und MSVC im Godbolt-Compiler-Explorer.

x86-64 gcc -O3 -std=gnu++11 -mpopcnt gibt Folgendes aus:

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 gibt (für die int arg-Version) aus:

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

Diese Quelle ist nicht x86-spezifisch oder GNU-spezifisch, sondern kann nur für x86 mit gcc/clang/icc gut kompiliert werden.

Beachten Sie auch, dass der Rückfall von gcc für Architekturen ohne Popcount mit nur einer Anweisung eine Byteweise-Tabellensuche ist. Das ist nicht wunderbar zum Beispiel für ARM .

198

Meiner Meinung nach ist die "beste" Lösung diejenige, die von einem anderen Programmierer (oder dem ursprünglichen Programmierer zwei Jahre später) ohne umfangreiche Kommentare gelesen werden kann. Vielleicht möchten Sie die schnellste oder klügste Lösung, die einige bereits bereitgestellt haben, aber ich bevorzuge die Lesbarkeit der Klugheit.

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;
}

Wenn Sie mehr Geschwindigkeit wünschen (und davon ausgehen, dass Sie dies gut dokumentieren, um Ihre Nachfolger zu unterstützen), können Sie eine Tabellensuche verwenden:

// 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);
}

Obwohl diese auf bestimmten Datentypgrößen basieren, sind sie nicht so portabel. Da jedoch viele Leistungsoptimierungen ohnehin nicht portabel sind, ist dies möglicherweise kein Problem. Wenn Sie Portabilität wollen, würde ich mich an die lesbare Lösung halten.

172
paxdiablo

Aus Hackers Freude, p. 66, Abbildung 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;
}

Führt in ~ 20-ish-Anweisungen (Arch-abhängig) aus, keine Verzweigung.

Hacker's Delightist entzückend! Sehr empfehlenswert.

95
Kevin Little

Ich denke, der schnellste Weg - ohne Verwendung von Nachschlagetabellen und popcount - ist der folgende. Es zählt die gesetzten Bits mit nur 12 Operationen.

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;
}

Es funktioniert, weil Sie die Gesamtzahl der gesetzten Bits zählen können, indem Sie die Hälfte der gesetzten Bits teilen, die Anzahl der gesetzten Bits in beiden Hälften zählen und sie dann addieren. Bekannt auch als Divide and Conquer-Paradigma. Lass uns ins Detail gehen .. 

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

Die Anzahl der Bits in zwei Bits kann 0b00, 0b01 oder 0b10 sein. Lass uns versuchen, dies auf 2 Bits zu berechnen. 

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

Dies war erforderlich: Die letzte Spalte zeigt die Anzahl der gesetzten Bits in jedem 2-Bit-Paar. Wenn die Zwei-Bit-Nummer >= 2 (0b10) ist, dann erzeugt and0b01, andernfalls 0b00

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

Diese Aussage sollte leicht verständlich sein. Nach der ersten Operation haben wir die Anzahl der gesetzten Bits in allen zwei Bits, jetzt summieren wir diese Zählung in allen 4 Bits.

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

Wir summieren dann das obige Ergebnis und geben uns die Gesamtzahl der gesetzten Bits in 4 Bits. Die letzte Aussage ist am schwierigsten.

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

Lass es uns weiter aufschlüsseln ... 

v + (v >> 4)

Es ist der zweiten Aussage ähnlich; Stattdessen zählen wir die gesetzten Bits in 4er-Gruppen. Wir wissen - aufgrund unserer vorherigen Operationen -, dass jedes Nibble die Anzahl der gesetzten Bits enthält. Schauen wir uns ein Beispiel an. Angenommen, wir haben das Byte 0b01000010. Dies bedeutet, dass das erste Halbbyte mit 4 Bits und das zweite mit 2bits eingestellt ist. Jetzt fügen wir diese Nibbles zusammen. 

0b01000010 + 0b01000000

Es gibt uns die Anzahl der gesetzten Bits in einem Byte, im ersten Halbbyte 0b01100010, und wir maskieren die letzten vier Bytes aller Bytes in der Zahl (verwerfen sie).

0b01100010 & 0xF0 = 0b01100000

Jetzt enthält jedes Byte die Anzahl der gesetzten Bits. Wir müssen sie alle zusammenfassen. Der Trick besteht darin, das Ergebnis mit 0b10101010 zu multiplizieren, das eine interessante Eigenschaft hat. Wenn unsere Nummer vier Bytes hat, A B C D, ergibt sich eine neue Nummer mit diesen Bytes A+B+C+D B+C+D C+D D. Bei einer 4-Byte-Nummer können maximal 32 Bit gesetzt werden, die als 0b00100000 dargestellt werden können.

Jetzt brauchen wir nur noch das erste Byte, das die Summe aller gesetzten Bits in allen Bytes enthält, und wir erhalten es durch >> 24. Dieser Algorithmus wurde für 32 bit-Wörter entwickelt, kann jedoch leicht für 64 bit-Wörter geändert werden.

73
vidit

Wenn Sie Java verwenden, wird dies durch die integrierte Methode Integer.bitCount erledigt.

54
Noether

Mir wurde langweilig und ich habe eine Milliarde Iterationen von drei Ansätzen gemacht. Compiler ist gcc -O3. CPU ist das, was sie in das Macbook Pro der ersten Generation stecken.

Am schnellsten geht es mit 3,7 Sekunden:

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] );
}

Der zweite Platz bezieht sich auf den gleichen Code, sucht aber 4 Byte anstelle von 2 Halbwörtern. Das dauerte etwa 5,5 Sekunden.

Der dritte Platz geht an den etwas verdrehten Ansatz der "seitlichen Hinzufügung", der 8,6 Sekunden dauerte.

Der vierte Platz geht an __builtin_popcount () von GCC mit beschämenden 11 Sekunden.

Das Zählen von einem Bit auf einmal war etwas langsamer, und es wurde mir langweilig, auf den Abschluss zu warten.

Wenn Sie also vor allem Wert auf Leistung legen, verwenden Sie den ersten Ansatz. Wenn Sie Interesse haben, aber nicht genug sind, um 64 KB RAM dafür auszugeben, verwenden Sie den zweiten Ansatz. Verwenden Sie andernfalls den lesbaren (aber langsamen) Ansatz für ein Bit nach dem anderen.

Es ist schwer, sich eine Situation vorzustellen, in der Sie den etwas verwirrenden Ansatz verwenden möchten.

Edit: Ähnliche Ergebnisse hier .

53
Mike F
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;
}

Lassen Sie mich diesen Algorithmus erklären.

Dieser Algorithmus basiert auf dem Divide- und Conquer-Algorithmus. Angenommen, es gibt eine 8-Bit-Ganzzahl 213 (binär 11010101), arbeitet der Algorithmus folgendermaßen (jedes Mal zwei Nachbarblöcke zusammenführen):

+-------------------------------+
| 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)
+-------------------------------+
29
abcdabcd987

Dies ist eine der Fragen, bei denen es hilfreich ist, Ihre Mikroarchitektur zu kennen. Ich habe gerade zwei Varianten unter gcc 4.3.3 getimpt, die mit -O3 unter Verwendung von C++ - Inlines kompiliert wurden, um den Overhead von Funktionsaufrufen und eine Milliarde Iterationen zu eliminieren. Takt genau). 

 inline int pop2 (vorzeichenloses x, vorzeichenloses 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; 
} 

Der unmodifizierte Hacker's Delight benötigte 12,2 Gigazyklen. Meine parallele Version (doppelt so viele Bits) läuft in 13,0 Gigazyklen. Bei einem 2,4-GHz-Core Duo verstrichen beide zusammen für beide. 25 Gigazyklen = etwas mehr als 10 Sekunden bei dieser Taktfrequenz, daher bin ich zuversichtlich, dass mein Timing stimmt. 

Dies hat mit Anweisungsabhängigkeitsketten zu tun, die für diesen Algorithmus sehr schlecht sind. Ich konnte die Geschwindigkeit wieder fast verdoppeln, indem ich ein Paar von 64-Bit-Registern verwendete. Wenn ich klug wäre und x + y etwas früher hinzufügte, konnte ich einige Schichten abschneiden. Die 64-Bit-Version mit einigen kleinen Anpassungen würde ungefähr gerade rauskommen, aber doppelt so viele Bits zählen. 

Mit 128-Bit-SIMD-Registern sind es noch ein Faktor zwei, und die Befehlssätze SSE haben oft auch clevere Abkürzungen. 

Es gibt keinen Grund dafür, dass der Code besonders transparent ist. Die Schnittstelle ist einfach, der Algorithmus kann an vielen Stellen online abgerufen werden und ist für umfassende Komponententests geeignet. Der Programmierer, der darauf stößt, kann sogar etwas lernen. Diese Bitoperationen sind auf Maschinenebene äußerst natürlich. 

OK, ich entschied mich für die optimierte 64-Bit-Version. Für dieses eine sizeof (ohne Vorzeichen lang) == 8 

 inline int pop2 (vorzeichenloses langes x, vorzeichenloses langes y) 
 {
 x = x - ((x >> 1) & 0x5555555555555555); 
 y = y - ((y >> 1) & 0x5555555555555555); 
 x = (x & 0x33333333333333) + ((x >> 2) & 0x3333333333333333; 
 y = (y & 0x33333333333333) + ((y >> 2) & 0x3333333333333333 ;
 x = (x + (x >> 4)) & 0x0F0F0F0F0F0F0F0F0F; 
 y = (y + (y >> 4)) & 0x0F0F0F0F0F0F0F0F0F; 
 x = x + y; 
 x = x + (x >> 8); 
 x = x + (x >> 16); 
 x = x + (x >> 32); 
 Rückgabe von x & 0xFF; 
} 

Das sieht gut aus (ich teste aber nicht sorgfältig). Jetzt liegen die Zeiten bei 10,70 Gigazyklen/14,1 Gigazyklen. Diese spätere Zahl summierte sich auf 128 Milliarden Bits und entspricht 5,9 Sekunden, die auf dieser Maschine vergangen sind. Die nicht parallele Version ist etwas schneller, weil ich im 64-Bit-Modus arbeite und 64-Bit-Register mag, die etwas besser sind als 32-Bit-Register. 

Mal sehen, ob es hier ein bisschen mehr OOO Pipelining gibt. Das war etwas komplizierter, also habe ich ein bisschen getestet. Jeder Begriff allein summiert sich auf 64, alle zusammen auf 256. 

 inline int pop4 (unsigniertes langes x, unsigniertes langes y, 
 unsigniertes langes u, unsigniertes langes v) 
 {
 Aufzählung {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); 
 Rückgabe x & 0x000001FF; 
} 

Ich war für einen Moment aufgeregt, aber es stellt sich heraus, dass gcc Inline-Tricks mit -O3 spielt, auch wenn ich das Inline-Keyword in einigen Tests nicht verwende. Wenn ich gcc Tricks spielen lasse, dauert eine Milliarde Aufrufe für pop4 () 12,56 Gigazyklen, aber ich stellte fest, dass es Argumente als konstante Ausdrücke faltete. Eine realistischere Zahl scheint 19,6 g für weitere 30% zu sein. Meine Testschleife sieht jetzt so aus, um sicherzustellen, dass jedes Argument unterschiedlich genug ist, um zu verhindern, dass gcc Tricks spielt. 

 hitime b4 = rdtsc (); 
 für (vorzeichenlose Länge i = 10L * 1000 * 1000 * 1000; i <11L * 1000 * 1000 * 1000; ++ i) 
 sum + = pop4 (i, i ^ 1, ~ i, i | 1); 
 hitime e4 = rdtsc (); 
256 Milliarden Bits wurden in 8.17s zusammengefasst. Arbeitet auf 1,02s für 32 Millionen Bits, wie in der 16-Bit-Tabellensuche verglichen. Kann nicht direkt verglichen werden, da die andere Bank nicht die Taktfrequenz angibt, aber ich glaube, dass ich die 64-KB-Tabellenedition geknackt habe.

Update: beschlossen, das Offensichtliche zu tun und pop6 () durch Hinzufügen von vier weiteren duplizierten Zeilen zu erstellen. Kam zu 22,8 gc, 384 Milliarden Bits summierten sich in 9,5 Sekunden. Es gibt also noch 20% Jetzt bei 800 ms für 32 Milliarden Bits. 

Update: decided to do the obvious and create pop6() by adding four more duplicated lines. Came out to 22.8gc, 384 billion bits summed in 9.5s elapsed. So there's another 20% Now at 800ms for 32 billion bits.

28
user183351

Warum nicht iterativ durch 2 teilen?

 count = 0 
, während n> 0 
 if (n% 2) == 1 
 count + = 1 
 n/= 2 

Ich stimme zu, dass dies nicht das schnellste ist, aber "best" ist etwas mehrdeutig. Ich würde jedoch argumentieren, dass "das Beste" ein Element der Klarheit haben sollte

25
daniel

Das Bit-Twiddling von Hacker's Delight wird so viel klarer, wenn Sie die Bitmuster ausschreiben. 

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;
}

Der erste Schritt addiert die geraden Bits zu den ungeraden Bits, wobei jeweils eine Summe von Bits erzeugt wird. In den anderen Schritten werden Chunks höherer Ordnung zu Chunks niedrigerer Ordnung hinzugefügt, wobei die Größe des Blocks insgesamt verdoppelt wird, bis die endgültige Zählung das gesamte Int.

20
John Dimm

Für ein glückliches Medium zwischen einer 232 Lookup-Tabelle und jedes Bit einzeln durchlaufen:

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;
}

Von http://ctips.pbwiki.com/CountBits

19
PhirePhly

Es ist nicht die schnellste oder beste Lösung, aber ich habe die gleiche Frage auf meine Art gefunden und habe angefangen zu denken und nachzudenken. Endlich wurde mir klar, dass dies so gemacht werden kann, wenn Sie das Problem von mathematischer Seite bekommen und eine Grafik zeichnen. Dann stellen Sie fest, dass es sich um eine Funktion handelt, die einen periodischen Teil hat, und dann erkennen Sie den Unterschied zwischen den Perioden ... also Bitte schön:

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);
    }
}
16
Peter

Dies kann in O(k) erfolgen, wobei k die Anzahl der gesetzten Bits ist.

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

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

    return count;
}
16
herohuyongtao

Die Funktion, nach der Sie suchen, wird oft als "Seitensumme" oder "Bevölkerungszahl" einer Binärzahl bezeichnet. Knuth erörtert es in Vorfascicle 1A, S. 11-12 (obwohl in Band 2, 4.6.3- (7) eine kurze Referenz vorhanden war.)

Der locus classicus ist Peter Wegners Artikel "Eine Technik zum Zählen von Einzelpersonen in einem binären Computer" aus der Communications der ACM, Band 3 (1960) Nummer 5, Seite 322 . Er gibt zwei verschiedene Algorithmen an, einen für Zahlen, die als "spärlich" (d. H. Eine kleine Anzahl von Einsen) zu erwarten sind, und einen für den umgekehrten Fall optimiert.

10
Michael Dorfman
  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;
    }
9
stacktay

Einige offene Fragen: -

  1. Wenn die Zahl dann negativ ist?
  2. Wenn die Zahl 1024 ist, wird die Methode "iterativ durch 2 teilen" zehnmal iteriert.

wir können den Algorithmus so ändern, dass die negative Zahl wie folgt unterstützt wird:

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

um das zweite Problem zu überwinden, können wir den Algorithmus wie folgt schreiben: -

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

für vollständige referenz siehe:

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

9
Baban

Ich verwende den folgenden Code, der intuitiver ist.

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

Logik: n & (n-1) setzt das zuletzt gesetzte Bit von n zurück.

PS: Ich weiß, das ist keine O(1) Lösung, wenn auch eine interessante Lösung.

8
Manish Mulani

Ich denke, die Brian Kernighans - Methode wird auch nützlich sein ....__ Sie durchläuft so viele Iterationen, wie festgelegte Bits vorhanden sind. Wenn wir also ein 32-Bit-Word haben, bei dem nur das High-Bit gesetzt ist, wird es nur einmal durch die Schleife gehen. 

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; 
}

Die Programmiersprache C, 2nd Ed. (von Brian W. Kernighan und Dennis M. Ritchie) erwähnt dies in Übung 2-9. Am 19. April 2006 wies Don Knuth darauf hin, dass diese Methode "erstmals von Peter Wegner in CACM 3 (1960), 322 veröffentlicht wurde. (Auch unabhängig von Derrick Lehmer entdeckt und 1964 in einem von Beckenbach herausgegebenen Buch veröffentlicht)."

8
Erorr

Was meinst du mit "Bester Algorithmus"? Der Kurzschlusscode oder der Schnellcode? Ihr Code sieht sehr elegant aus und hat eine konstante Ausführungszeit. Der Code ist auch sehr kurz.

Aber wenn die Geschwindigkeit der Hauptfaktor ist und nicht die Codegröße, dann denke ich, dass das Folgende schneller sein kann:

       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 );
        }

Ich denke, dass dies für einen 64-Bit-Wert nicht schneller ist, aber ein 32-Bit-Wert kann schneller sein.

7
Horcrux7

wenn Sie C++ verwenden, können Sie die Metaprogrammierung von Vorlagen verwenden.

// 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;
}

verwendung wäre:

// 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 )

sie können diese Vorlage natürlich noch erweitern, um verschiedene Typen zu verwenden (sogar die Bitgröße für die automatische Erkennung), aber ich habe es aus Gründen der Übersichtlichkeit einfach gehalten.

edit: vergessen zu erwähnen, dass dies gut ist, da sollte in einem beliebigen C++ - Compiler funktionieren und es im Grunde nur die Schleife für Sie abrollt, wenn ein konstanter Wert für die Bitanzahl verwendet wird (mit anderen Worten: Ich bin mir ziemlich sicher, dass es die schnellste Methode ist, die Sie finden werden.

7
pentaphobe

Ich habe ungefähr 1990 ein schnelles Bitcount-Makro für RISC-Maschinen geschrieben. Es verwendet keine erweiterte Arithmetik (Multiplikation, Division,%), Speicherabrufe (viel zu langsam), Verzweigungen (viel zu langsam), aber es geht davon aus, dass die CPU über ein 32-Bit-Barrel-Shifter (mit anderen Worten, >> 1 und >> 32 benötigen die gleiche Anzahl von Zyklen.) Es wird davon ausgegangen, dass kleine Konstanten (wie 6, 12, 24) nichts kosten, um in die Register geladen zu werden oder gespeichert werden in temporären und immer und immer wieder verwendet.

Mit diesen Annahmen zählt es 32 Bits in etwa 16 Zyklen/Anweisungen auf den meisten RISC-Maschinen. Beachten Sie, dass 15 Anweisungen/Zyklen nahe an einer unteren Grenze für die Anzahl der Zyklen oder Anweisungen liegen, da es anscheinend mindestens 3 Anweisungen (Maske, Shift, Operator) benötigt, um die Anzahl der Addends zu halbieren, so log_2 (32) = 5, 5 x 3 = 15 Anweisungen sind quasi niedergebunden.

#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;

Hier ist ein Geheimnis für den ersten und komplexesten Schritt:

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

wenn ich also die 1. Spalte (A) oben nehme, sie um 1 Bit nach rechts schiebe und sie von AB subtrahiere, erhalte ich die Ausgabe (CD). Die Erweiterung auf 3 Bit ist ähnlich; Sie können es mit einem 8-reihigen booleschen Tisch wie meiner oben überprüfen, wenn Sie möchten.

  • Don Gillies
7
systemBuilder

Ich verwende dies immer im Wettbewerbsprogramm und es ist einfach zu schreiben und effizient:

#include <bits/stdc++.h>

using namespace std;

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

Ich habe eine Implementierung der Bitzählung in einem Array mit SIMD-Befehl (SSSE3 und AVX2) gefunden. Es ist in 2 bis 2,5 mal besser als wenn __popcnt64 intrinsic Funktion verwendet wird.

SSSE3-Version:

#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-Version:

#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];
}
6
ErmIg

Dieses Beispiel gefällt mir besonders gut aus der Fortune-Datei:

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

Ich mag es am besten, weil es so hübsch ist!

6
Ross

Java JDK1.5

Integer.bitCount (n);

dabei ist n die Zahl, deren Einsen gezählt werden sollen.

überprüfen sie auch,

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);
         }
6
Rahul

Hier ist ein tragbares Modul (ANSI-C), mit dem Sie jeden Ihrer Algorithmen in jeder Architektur vergleichen können. 

Ihre CPU hat 9 Bit Bytes? Kein Problem :-) Derzeit werden 2 Algorithmen, der K & R-Algorithmus und eine byteweise Nachschlagetabelle implementiert. Die Nachschlagetabelle ist im Durchschnitt dreimal schneller als der K & R-Algorithmus. Wenn jemand einen Weg finden kann, den Algorithmus "Hacker's Delight" portabel zu machen, können Sie ihn gerne hinzufügen.

#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
5

Es gibt viele Algorithmen, um die gesetzten Bits zu zählen; aber ich denke, der beste ist der schnellere! Sie können die Details auf dieser Seite sehen:

Bit Twiddling Hacks

Ich schlage folgendes vor:

Zählbits, die in 14, 24 oder 32-Bit-Wörtern mit 64-Bit-Anweisungen gesetzt sind

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;

Dieses Verfahren erfordert eine 64-Bit-CPU mit schneller Modulteilung, um effizient zu sein. Die erste Option erfordert nur drei Vorgänge. die zweite Option dauert 10; und die dritte Option dauert 15. 

5
Mostafa

Schnelle C # -Lösung mit vorberechneten Byte-Bit-Zählwerten mit Verzweigung der Eingangsgröße.

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
    };
}
5
dadhi

32-Bit oder nicht? Ich kam gerade mit dieser Methode in Java, nachdem ich gelesen hatte " cracking the coding interview " 4th Edition - Übung 5.5 (Kap. 5: Bitmanipulation). Wenn das niedrigstwertige Bit 1 Inkrement count ist, verschieben Sie die Ganzzahl nach rechts.

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

Ich denke, dieser ist intuitiver als die Lösungen mit konstantem 0x33333333, egal wie schnell sie sind. Es hängt von Ihrer Definition des "besten Algorithmus" ab.

4
Raymond Chenon

was Sie tun können, ist

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

die Logik dahinter ist, dass die Bits von n-1 vom ganz rechts gesetzten Bit von n invertiert werden. Wenn n = 6, d. h. 110, dann ist 5 101, werden die Bits vom ganz rechts gesetzten Bit von n invertiert. Wenn wir & diese beiden also das am weitesten rechts stehende Bit 0 in jeder Iteration machen und immer zum am weitesten rechts stehenden gesetzten Bit gehen. Von dort aus wird das gesetzte Bit gezählt. Die schlechteste Zeitkomplexität ist O(logn), wenn jedes Bit gesetzt ist.

3
Varun Gusain

Ich persönlich benutze das:

  public static int myBitCount(long L){
      int count = 0;
      while (L != 0) {
         count++;
         L ^= L & -L; 
      }
      return count;
  }
2
SteveR
int bitcount(unsigned int n)
{ 
      int count=0;
      while(n)
      {
           count += n & 0x1u;
           n >>= 1;
      }
      return  count;
 }

Der iterierte 'count' läuft zeitlich proportional zur Gesamtzahl der Bits. Es durchläuft einfach alle Bits und endet aufgrund der while-Bedingung etwas früher. Nützlich, wenn 1'S oder die gesetzten Bits sparse und zwischen niedrigstwertige Bits sind.

1
Mufaddal Kagda

Ein weiterer Hamming-Gewichtsalgorithmus, wenn Sie sich auf einer BMI2-fähigen CPU befinden

the_weight=__tzcnt_u64(~_pext_u64(data[i],data[i]));

Habe Spaß!

1

Sie können die integrierte Funktion __builtin_popcount () verwenden. In C++ ist kein _builtin_popcount vorhanden, es ist jedoch eine integrierte Funktion des GCC-Compilers. Diese Funktion gibt die Anzahl der gesetzten Bits in einer Ganzzahl zurück.

int __builtin_popcount (unsigned int x);

Referenz: Bit Twiddling Hacks

1
rashedcs
int countBits(int x)
{
    int n = 0;
    if (x) do n++;
           while(x=x&(x-1));
    return n;
}   

Oder auch:

int countBits(int x) { return (x)? 1+countBits(x&(x-1)): 0; }
1
abelenky

In Java 8 oder 9 rufen Sie einfach Integer.bitCount auf.

Hier ist eine Lösung, die bisher nicht erwähnt wurde, unter Verwendung von Bitfeldern. Das folgende Programm zählt die gesetzten Bits in einem Array von 100000000-16-Bit-Ganzzahlen mit 4 verschiedenen Methoden. Die Timing-Ergebnisse sind in Klammern angegeben (unter MacOSX mit gcc -O3):

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

#define LENGTH 100000000

typedef struct {
    unsigned char bit0 : 1;
    unsigned char bit1 : 1;
    unsigned char bit2 : 1;
    unsigned char bit3 : 1;
    unsigned char bit4 : 1;
    unsigned char bit5 : 1;
    unsigned char bit6 : 1;
    unsigned char bit7 : 1;
} bits;

unsigned char sum_bits(const unsigned char x) {
    const bits *b = (const bits*) &x;
    return b->bit0 + b->bit1 + b->bit2 + b->bit3 \
         + b->bit4 + b->bit5 + b->bit6 + b->bit7;
}

int NumberOfSetBits(int i) {
    i = i - ((i >> 1) & 0x55555555);
    i = (i & 0x33333333) + ((i >> 2) & 0x33333333);
    return (((i + (i >> 4)) & 0x0F0F0F0F) * 0x01010101) >> 24;
}

#define out(s) \
    printf("bits set: %lu\nbits counted: %lu\n", 8*LENGTH*sizeof(short)*3/4, s);

int main(int argc, char **argv) {
    unsigned long i, s;
    unsigned short *x = malloc(LENGTH*sizeof(short));
    unsigned char lut[65536], *p;
    unsigned short *ps;
    int *pi;

    /* set 3/4 of the bits */
    for (i=0; i<LENGTH; ++i)
        x[i] = 0xFFF0;

    /* sum_bits (1.772s) */
    for (i=LENGTH*sizeof(short), p=(unsigned char*) x, s=0; i--; s+=sum_bits(*p++));
    out(s);

    /* NumberOfSetBits (0.404s) */
    for (i=LENGTH*sizeof(short)/sizeof(int), pi=(int*)x, s=0; i--; s+=NumberOfSetBits(*pi++));
    out(s);

    /* populate lookup table */
    for (i=0, p=(unsigned char*) &i; i<sizeof(lut); ++i)
        lut[i] = sum_bits(p[0]) + sum_bits(p[1]);

    /* 256-bytes lookup table (0.317s) */
    for (i=LENGTH*sizeof(short), p=(unsigned char*) x, s=0; i--; s+=lut[*p++]);
    out(s);

    /* 65536-bytes lookup table (0.250s) */
    for (i=LENGTH, ps=x, s=0; i--; s+=lut[*ps++]);
    out(s);

    free(x);
    return 0;
}

Während die Bitfield-Version sehr gut lesbar ist, zeigen die Timing-Ergebnisse, dass sie über 4x langsamer ist als NumberOfSetBits(). Die auf Nachschlagetabellen basierenden Implementierungen sind insbesondere mit einer 65-KB-Tabelle noch ein bisschen schneller.

1
Stefan

Hier ist der Beispielcode, der nützlich sein kann.

private static final int[] bitCountArr = new int[]{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};
private static final int firstByteFF = 255;
public static final int getCountOfSetBits(int value){
    int count = 0;
    for(int i=0;i<4;i++){
        if(value == 0) break;
        count += bitCountArr[value & firstByteFF];
        value >>>= 8;
    }
    return count;
}

C++ 20 std::popcount

Der folgende Vorschlag wurde zusammengeführt http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2019/p0553r4.html und sollte dem <bit>-Header hinzugefügt werden.

Ich erwarte die Verwendung wie folgt:

#include <bit>
#include <iostream>

int main() {
    std::cout << std::popcount(0x55) << std::endl;
}

Ich werde es versuchen, wenn GCC unterstützt wird. GCC 9.1.0 mit g++-9 -std=c++2a unterstützt es immer noch nicht.

In dem Vorschlag heißt es:

Header: <bit>

namespace std {

  // 25.5.6, counting
  template<class T>
    constexpr int popcount(T x) noexcept;

und:

template<class T>
  constexpr int popcount(T x) noexcept;

Einschränkungen: T ist ein Integer-Typ ohne Vorzeichen (3.9.1 [basic.fundamental]).

Rückgabe: Die Anzahl von 1 Bits im Wert von x.

std::rotl und std::rotr wurden ebenfalls hinzugefügt, um kreisförmige Bit-Rotationen durchzuführen: Best Practices für kreisförmige Verschiebungs- (Rotations-) Operationen in C++

#!/user/local/bin/Perl


    $c=0x11BBBBAB;
     $count=0;
     $m=0x00000001;
    for($i=0;$i<32;$i++)
    {
        $f=$c & $m;
        if($f == 1)
        {
            $count++;
        }
        $c=$c >> 1;
    }
    printf("%d",$count);

ive done it through a Perl script. the number taken is $c=0x11BBBBAB   
B=3 1s   
A=2 1s   
so in total  
1+1+3+3+3+2+3+3=19
0
dhpant28

Ich habe diesen Ansatz nirgendwo gesehen:

int nbits(unsigned char v) {
    return ((((v - ((v >> 1) & 0x55)) * 0x1010101) & 0x30c00c03) * 0x10040041) >> 0x1c;
}

Es arbeitet pro Byte, also müsste es für eine 32-Bit-Ganzzahl viermal aufgerufen werden. Sie wird von der Seitwärtsaddition abgeleitet, verwendet jedoch zwei 32-Bit-Multiplikationen, um die Anzahl der Befehle auf nur 7 zu reduzieren.

Die meisten aktuellen C-Compiler optimieren diese Funktion mithilfe von SIMD (SSE2) -Anweisungen, wenn klar ist, dass die Anzahl der Anforderungen ein Vielfaches von 4 beträgt und sie durchaus wettbewerbsfähig wird. Es ist portabel, kann als Makro- oder Inline-Funktion definiert werden und benötigt keine Datentabellen.

Dieser Ansatz kann mit 64-Bit-Multiplikationen auf jeweils 16 Bits erweitert werden. Es schlägt jedoch fehl, wenn alle 16 Bits gesetzt sind, und gibt 0 zurück. Daher kann es nur verwendet werden, wenn der 0xffff-Eingabewert nicht vorhanden ist. Es ist auch langsamer aufgrund der 64-Bit-Operationen und optimiert nicht gut.

0
cipilo

Folgendes funktioniert in PHP (alle PHP -Integer sind 32-Bit-Vorzeichen, dieses 31-Bit):

function bits_population($nInteger)
{

    $nPop=0;
    while($nInteger)
    {
        $nInteger^=(1<<(floor(1+log($nInteger)/log(2))-1));
        $nPop++;
    }
    return $nPop;
}
0

Wie wäre es, die Ganzzahl in eine Binärzeichenfolge umzuwandeln und die Einsen zu zählen?

pHP-Lösung:

substr_count( decbin($integer), '1' );
0
KeineKaefer

Einfacher Algorithmus zum Zählen der Anzahl der gesetzten Bits:

int countbits(n){
     int count = 0;
     while(n != 0){
        n = n & (n-1);
        count++;
   }
   return count;
}

Nehmen Sie das Beispiel von 11 (1011) und versuchen Sie, den Algorithmus manuell zu durchlaufen. Sollte dir sehr helfen!

0
Arjun Singh

Ein einfacher Weg, der für eine kleine Anzahl von Bits gut funktionieren sollte, ist ungefähr so ​​(für 4 Bits in diesem Beispiel):

(i & 1) + (i & 2)/2 + (i & 4)/4 + (i & 8)/8

Würden andere dies als einfache Lösung für eine kleine Anzahl von Bits empfehlen?

0