8 bits représentant le nombre 7 ressemblent à ceci:
00000111
Trois bits sont définis.
Quels sont les algorithmes permettant de déterminer le nombre de bits définis dans un entier de 32 bits?
Ceci est connu sous le nom de " Hamming Weight ", "popcount" ou "addition latérale".
Le "meilleur" algorithme dépend vraiment du processeur sur lequel vous vous trouvez et de votre modèle d'utilisation.
Certains processeurs ont une seule instruction intégrée pour le faire et d'autres ont des instructions parallèles qui agissent sur des vecteurs de bits. Les instructions parallèles (comme popcnt
de x86, sur les CPU où il est supporté) seront presque certainement les plus rapides. Certaines autres architectures peuvent avoir une instruction lente implémentée avec une boucle microcodée qui teste un bit par cycle (citation nécessaire () ).
Une méthode de recherche de table préremplie peut être très rapide si votre CPU dispose d'un cache volumineux et/ou si vous suivez ces instructions en boucle serrée. Toutefois, cela peut être pénalisant en raison des dépenses occasionnées par un "cache miss", dans lequel le processeur doit extraire une partie de la table de la mémoire principale.
Si vous savez que vos octets seront principalement des 0 ou des 1, il existe des algorithmes très efficaces pour ces scénarios.
Je crois qu'un très bon algorithme à usage général est le suivant, connu sous le nom d'algorithme SWAR "à précision variable". Je l'ai exprimé dans un pseudo langage de type C, vous devrez peut-être l'ajuster pour fonctionner pour un langage particulier (par exemple, en utilisant uint32_t pour C++ et >>> en 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;
}
C’est le meilleur comportement dans le pire des cas de l’un des algorithmes décrits, ce qui permet de traiter efficacement tout modèle d’utilisation ou toutes les valeurs que vous lui soumettez.
Cet algorithme bit-SWAR pourrait être mis en parallèle dans plusieurs éléments vectoriels à la fois, plutôt que dans un seul registre entier, pour accélérer les CPU avec SIMD, mais sans instruction popcount utilisable. (Par exemple, un code x86-64 devant s’exécuter sur n’importe quel processeur, pas seulement Nehalem ou une version ultérieure.)
Cependant, la meilleure façon d'utiliser des instructions vectorielles pour popcount consiste généralement à utiliser une méthode de lecture aléatoire pour effectuer une recherche de table sur 4 bits à la fois de chaque octet en parallèle. (Les 4 bits indexent une table de 16 entrées dans un registre vectoriel).
Sur les processeurs Intel, l’instruction 64 bits popcnt matérielle peut être supérieure à une implémentation SSSE3 PSHUFB
bit-parallel d’un facteur environ 2, mais seulement si votre compilateur l’a parfaitement adaptée . Sinon, SSE peut sortir considérablement. Les versions les plus récentes du compilateur sont conscientes de la dépendance popcnt falseproblème sur Intel .
Références:
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)
Pensez également aux fonctions intégrées de vos compilateurs.
Sur le compilateur GNU par exemple, vous pouvez simplement utiliser:
int __builtin_popcount (unsigned int x);
int __builtin_popcountll (unsigned long long x);
Dans le pire des cas, le compilateur générera un appel à une fonction. Dans le meilleur des cas, le compilateur émettra une instruction cpu pour effectuer le même travail plus rapidement.
Les composants intrinsèques de GCC fonctionnent même sur plusieurs plates-formes. Popcount deviendra la norme dans l'architecture x86, il est donc logique de commencer à utiliser l'intrinsèque maintenant. D'autres architectures ont le popcount depuis des années.
Sur x86, vous pouvez indiquer au compilateur qu'il peut assumer la prise en charge de l'instruction popcnt
avec -mpopcnt
ou -msse4.2
afin d'activer également les instructions de vecteur ajoutées à la même génération. Voir Options GCC x86 . -march=nehalem
(ou -march=
quel que soit le processeur que vous souhaitez associer à votre code) pourrait être un bon choix. L'exécution du fichier binaire résultant sur un processeur plus ancien entraînera une erreur d'instruction illégale.
Pour créer des fichiers binaires optimisés pour la machine sur laquelle vous les construisez, utilisez -march=native
(avec gcc, clang ou ICC).
MSVC fournit une instruction intrinsèque pour l'instruction x86 popcnt
, mais contrairement à gcc, elle est en réalité une caractéristique intrinsèque de l'instruction matérielle et nécessite une prise en charge matérielle.
Utilisation de std::bitset<>::count()
au lieu d'un intégré
En théorie, tout compilateur qui sait comment décompter efficacement le CPU cible devrait exposer cette fonctionnalité via ISO C++ std::bitset<>
. En pratique, mieux vaut utiliser le bit-hack ET/shift/ADD dans certains cas pour certains processeurs cibles.
Pour les architectures cibles où popcount est une extension facultative (telle que x86), tous les compilateurs ne disposent pas d'un std::bitset
qui en tire parti lorsqu'il est disponible. Par exemple, MSVC n'a aucun moyen d'activer le support popcnt
au moment de la compilation et utilise toujours une table lookup , même avec /Ox /Arch:AVX
(ce qui implique SSE4.2, bien que techniquement, il existe un bit de fonctionnalité distinct pour popcnt
.)
Mais au moins, vous obtenez quelque chose de portable qui fonctionne partout, et avec gcc/clang avec les bonnes options de cibles, vous obtenez un décompte matériel pour les architectures qui le prennent en charge.
#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();
}
Voir asm depuis gcc, clang, icc et MSVC dans l'explorateur du compilateur Godbolt.
x86-64 gcc -O3 -std=gnu++11 -mpopcnt
émet ceci:
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
émet (pour la version int
arg):
rldicl 3,3,0,32 # zero-extend from 32 to 64-bit
popcntd 3,3 # popcount
blr
Cette source n'est pas du tout spécifique à x86 ou à GNU, mais compile bien uniquement pour x86 avec gcc/clang/icc.
Notez également que la solution de secours de gcc pour les architectures sans popcount à instruction unique est une recherche de table octet à la fois. Ce n'est pas merveilleux pour ARM, par exemple .
À mon avis, la "meilleure" solution est celle qui peut être lue par un autre programmeur (ou le programmeur d'origine deux ans plus tard) sans commentaires copieux. Vous voudrez peut-être la solution la plus rapide ou la plus intelligente que certains ont déjà fournie, mais je préfère la lisibilité à l’intelligence en tout temps.
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;
}
Si vous voulez plus de rapidité (et en supposant que vous documentiez bien pour aider vos successeurs), vous pouvez utiliser une table de recherche:
// 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);
}
Bien que ceux-ci reposent sur des tailles de types de données spécifiques, ils ne sont pas aussi portables. Cependant, étant donné que de nombreuses optimisations de performances ne sont de toute façon pas portables, cela peut ne pas être un problème. Si vous voulez la portabilité, je me contenterais de la solution lisible.
Du délice du pirate, p. 66, Figure 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;
}
Exécute dans des instructions ~ 20-ish (dépendant de Arch), pas de branchement.
Le plaisir des piratesest délicieux! Hautement recommandé.
Je pense que le moyen le plus rapide - sans utiliser de tables de consultation et popcount - est le suivant. Il compte les bits définis avec seulement 12 opérations.
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;
}
Cela fonctionne parce que vous pouvez compter le nombre total de bits définis en les divisant en deux moitiés, en comptant le nombre de bits définis dans les deux moitiés, puis en les additionnant. Aussi connu comme paradigme Divide and Conquer
. Entrons dans les détails ..
v = v - ((v >> 1) & 0x55555555);
Le nombre de bits dans deux bits peut être 0b00
, 0b01
ou 0b10
. Essayons de résoudre ce problème sur 2 bits.
---------------------------------------------
| v | (v >> 1) & 0b0101 | v - x |
---------------------------------------------
0b00 0b00 0b00
0b01 0b00 0b01
0b10 0b01 0b01
0b11 0b01 0b10
C'est ce qui était requis: la dernière colonne indique le nombre de bits définis dans chaque paire de deux bits. Si le nombre à deux bits est >= 2 (0b10)
alors and
produit 0b01
, sinon il produira 0b00
.
v = (v & 0x33333333) + ((v >> 2) & 0x33333333);
Cette déclaration devrait être facile à comprendre. Après la première opération, nous avons le nombre de bits définis tous les deux bits. Nous récapitulons maintenant ce nombre tous les 4 bits.
v & 0b00110011 //masks out even two bits
(v >> 2) & 0b00110011 // masks out odd two bits
Nous résumons ensuite le résultat ci-dessus en nous donnant le nombre total de bits définis sur 4 bits. La dernière déclaration est la plus délicate.
c = ((v + (v >> 4) & 0xF0F0F0F) * 0x1010101) >> 24;
Faisons le décomposer plus loin ...
v + (v >> 4)
Cela ressemble à la deuxième déclaration. nous comptons à la place les bits définis dans des groupes de 4. Nous savons, en raison de nos opérations précédentes, que chaque nibble contient le nombre de bits définis. Regardons un exemple. Supposons que nous ayons l'octet 0b01000010
. Cela signifie que le premier quartet a ses 4bits et le second 2bits. Maintenant, nous ajoutons ces grignotements ensemble.
0b01000010 + 0b01000000
Il nous donne le nombre de bits définis dans un octet, dans le premier quartet 0b01100010
et, par conséquent, nous masquons les quatre derniers octets de tous les octets du nombre (en les supprimant).
0b01100010 & 0xF0 = 0b01100000
Maintenant, chaque octet contient le nombre de bits définis. Nous devons les additionner tous ensemble. L'astuce consiste à multiplier le résultat par 0b10101010
qui possède une propriété intéressante. Si notre numéro a quatre octets, A B C D
, il en résultera un nouveau numéro avec ces octets A+B+C+D B+C+D C+D D
. Un nombre de 4 octets peut avoir un maximum de 32 bits, pouvant être représenté par 0b00100000
.
Tout ce dont nous avons besoin maintenant, c'est du premier octet qui contient la somme de tous les bits définis dans tous les octets, et nous l'obtenons par >> 24
. Cet algorithme a été conçu pour les mots 32 bit
mais peut être facilement modifié pour les mots 64 bit
.
Si vous utilisez Java, la méthode intégrée Integer.bitCount
le fera.
Je me suis ennuyé et chronométré un milliard d'itérations de trois approches. Le compilateur est gcc -O3. Le CPU est ce qu’ils mettent dans le Macbook Pro 1ère génération.
Le plus rapide est le suivant, à 3,7 secondes:
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] );
}
La deuxième place revient au même code mais en recherchant 4 octets au lieu de 2 demi-mots. Cela a pris environ 5,5 secondes.
La troisième place revient à l'approche de «l'addition latérale» qui a pris 8,6 secondes.
La quatrième place revient à __builtin_popcount () de GCC, à 11 secondes honteuses.
L’approche consistant à compter un bit à la fois était beaucoup plus lente et je me suis ennuyé d’attendre que cela se termine.
Donc, si vous vous souciez avant tout de la performance, utilisez la première approche. Si vous y tenez, mais pas assez pour dépenser 64 Ko de RAM dessus, utilisez la deuxième approche. Sinon, utilisez l’approche lisible (mais lente), bit par bit.
Il est difficile de penser à une situation dans laquelle vous voudriez utiliser l'approche du «bidouillage».
Edit: Résultats similaires ici .
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;
}
Permettez-moi d'expliquer cet algorithme.
Cet algorithme est basé sur l'algorithme Divide and Conquer. Supposons qu'il existe un entier de 8 bits 213 (11010101 en binaire), l'algorithme fonctionne comme suit (à chaque fois que deux blocs voisins sont fusionnés):
+-------------------------------+
| 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)
+-------------------------------+
C'est l'une de ces questions où il est utile de connaître votre micro-architecture. Je viens de chronométrer deux variantes de la version 4.3.3 de gcc compilées avec -O3 en utilisant des lignes C++ pour éliminer le temps système d’appel de la fonction, un milliard d’itérations, en conservant la somme courante de tous les comptes pour que le compilateur ne supprime rien d’important, en utilisant rdtsc pour la synchronisation ( cycle d'horloge précis).
inline int pop2 (non signé x, non signé 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); retourne (x + y) & 0x000000FF; }
Le Hacker's Delight non modifié a pris 12,2 gigacycles. Ma version parallèle (qui compte deux fois plus de bits) fonctionne en 13,0 gigacycles. Un total de 10.5 secondes s'est écoulé pour les deux ensemble sur un Core Duo à 2,4 GHz. 25 gigacycles = un peu plus de 10 secondes à cette fréquence d'horloge, je suis donc convaincu que mon timing est correct.
Cela concerne les chaînes de dépendance des instructions, qui sont très mauvaises pour cet algorithme. Je pouvais presque doubler à nouveau la vitesse en utilisant une paire de registres 64 bits. En fait, si j’étais intelligent et que j’ajoutais x + y un peu plus tôt, je pouvais me débarrasser de certains changements. La version 64 bits, avec quelques petites modifications, sortirait à peu près égale, mais compterait encore deux fois plus de bits.
Avec les registres SIMD 128 bits, il s'agit d'un facteur encore deux et les jeux d'instructions SSE comportent souvent des raccourcis astucieux.
Il n'y a aucune raison pour que le code soit particulièrement transparent. L'interface est simple, l'algorithme peut être référencé en ligne à de nombreux endroits et se prête à un test unitaire complet. Le programmeur qui tombe dessus peut même apprendre quelque chose. Ces opérations de bits sont extrêmement naturelles au niveau de la machine.
D'accord, j'ai décidé de mettre à l'essai la version 64 bits modifiée. Pour ce one 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); renvoyer x & 0xFF; }
Cela semble à peu près correct (je ne teste pas soigneusement, cependant). Maintenant, les timings sortent à 10,70 gigacycles/14,1 gigacycles. Ce dernier chiffre a totalisé 128 milliards de bits et correspond à 5,9 secondes écoulées sur cette machine. La version non parallèle accélère un peu car je travaille en mode 64 bits et aime les registres 64 bits légèrement supérieurs aux registres 32 bits.
Voyons s'il y a un peu plus de OOO pipelins ici. C’était un peu plus compliqué, alors j’ai testé un peu. Chaque terme représente à lui seul 64, la somme de 256.
inline int pop4 (non signé long x, non signé long y, non signé long u, non signé long v) { énumération {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); renvoyer x & 0x000001FF; }
J'étais excité pendant un moment, mais il s'avère que gcc joue des tours inline avec -O3 même si je n'utilise pas le mot clé inline dans certains tests. Lorsque je laisse gcc jouer à des tours, un milliard d'appels à pop4 () prennent 12,56 gigacycles, mais j'ai déterminé qu'il s'agissait de plier des arguments en tant qu'expressions constantes. Un nombre plus réaliste semble être 19.6gc pour une autre accélération de 30%. Ma boucle de test ressemble maintenant à ceci: assurez-vous que chaque argument est suffisamment différent pour empêcher gcc de jouer des tours.
hitime b4 = rdtsc (); pour (longueur non signée i = 10L * 1000 * 1000 * 1000; i <11L * 1000 * 1000 * 1000; ++ i) somme + = pop4 (i, i ^ 1, ~ i, i | 1); hitime e4 = rdtsc (); 256 milliards de bits additionnés en 8.17 secondes. Fonctionne à 1.02s pour 32 millions de bits, comme indiqué dans la recherche de tableau 16 bits. Impossible de comparer directement, car l’autre banc ne donne pas une vitesse d’horloge, mais on dirait que j’ai tiré la morve de l’édition de table de 64 Ko, ce qui est une utilisation tragique du cache L1.
Mise à jour: a décidé de procéder à l'évidence et de créer pop6 () en ajoutant quatre lignes dupliquées supplémentaires. Sorti à 22,8gc, 384 milliards de bits additionnés en 9,5s se sont écoulés. Donc, il y a encore 20% Maintenant à 800 ms pour 32 milliards de 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.
Pourquoi ne pas diviser de façon itérative par 2?
compte = 0 tandis que n> 0 si (n% 2) == 1 compte + = 1 n/= 2
Je conviens que ce n'est pas le plus rapide, mais "le meilleur" est quelque peu ambigu. Je dirais que ce "meilleur" devrait avoir un élément de clarté
Le piratage de bits du pirate devient tellement plus clair lorsque vous écrivez les modèles de bits.
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;
}
La première étape ajoute les bits pairs aux bits impairs, produisant une somme de bits dans chaque deux. Les autres étapes ajoutent des morceaux d'ordre élevé aux morceaux d'ordre inférieur, en doublant la taille de morceau jusqu'à ce que le compte final prenne l'intégralité de l'int.
Pour un juste milieu entre 232 table de consultation et itération individuelle dans chaque bit:
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;
}
Ce n'est pas la solution la plus rapide ou la meilleure, mais j'ai trouvé la même question sur mon chemin et j'ai commencé à penser et à penser. Enfin, j'ai réalisé que cela peut se faire de la sorte si le problème est mathématique et que vous tracez un graphique, alors vous trouvez que c'est une fonction qui a une partie périodique, puis vous réalisez la différence entre les périodes ... Voici:
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);
}
}
Cela peut être fait dans O(k)
, où k
est le nombre de bits définis.
int NumberOfSetBits(int n)
{
int count = 0;
while (n){
++ count;
n = (n - 1) & n;
}
return count;
}
La fonction que vous recherchez est souvent appelée "somme latérale" ou "dénombrement" d'un nombre binaire. Knuth en parle dans le pré-fascicule 1A, pages 11-12 (bien qu'il y ait eu une brève référence dans le Volume 2, 4.6.3- (7).)
Le locus classicus est l'article de Peter Wegner intitulé "Une technique pour compter les ordinateurs dans un ordinateur binaire", extrait du Communications de l'ACM, volume 3 (1960) numéro 5, page 322 . Il y donne deux algorithmes différents, l'un optimisé pour les nombres censés être "clairsemés" (c'est-à-dire en avoir un petit nombre) et l'autre pour le cas contraire.
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;
}
Quelques questions ouvertes: -
nous pouvons modifier l'algo pour supporter le nombre négatif comme suit: -
count = 0
while n != 0
if ((n % 2) == 1 || (n % 2) == -1
count += 1
n /= 2
return count
maintenant pour surmonter le deuxième problème nous pouvons écrire l’algo comme: -
int bit_count(int num)
{
int count=0;
while(num)
{
num=(num)&(num-1);
count++;
}
return count;
}
pour une référence complète, voir:
J'utilise le code ci-dessous qui est plus intuitif.
int countSetBits(int n) {
return !n ? 0 : 1 + countSetBits(n & (n-1));
}
Logique: n & (n-1) réinitialise le dernier bit défini de n.
P.S: Je sais que ce n’est pas la solution O(1), bien qu’il s’agisse d’une solution intéressante.
Je pense que la méthode de Brian Kernighan sera utile aussi .... Elle passe par autant d'itérations qu'il y a de bits définis. Donc, si nous avons un mot 32 bits avec uniquement le bit fort, alors il ne passera qu'une fois dans la boucle.
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;
}
Publié en 1988, le langage de programmation C 2nd Ed. (par Brian W. Kernighan et Dennis M. Ritchie) le mentionne dans l’exercice 2-9. Le 19 avril 2006, Don Knuth m'a fait remarquer que cette méthode "a été publiée pour la première fois par Peter Wegner dans CACM 3 (1960), 322. (Découverte également de manière indépendante par Derrick Lehmer et publiée en 1964 dans un livre publié par Beckenbach."
Que voulez-vous dire par "meilleur algorithme"? Le code raccourci ou le code à jeun? Votre code a l’air très élégant et son temps d’exécution est constant. Le code est également très court.
Mais si la vitesse est le facteur majeur et non la taille du code, alors je pense que le suivi peut être plus rapide:
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 );
}
Je pense que cela ne sera pas plus rapide pour une valeur 64 bits, mais une valeur 32 bits peut être plus rapide.
si vous utilisez C++, une autre option consiste à utiliser la métaprogrammation des modèles:
// 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;
}
utilisation serait:
// 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 )
vous pouvez bien sûr développer davantage ce modèle pour utiliser différents types (même la taille de bit à détection automatique), mais je l’ai simplifié pour plus de clarté.
edit: j'ai oublié de mentionner que c'est bien parce que cela devrait fonctionner dans n'importe quel compilateur C++ et qu'il ne fait que dérouler votre boucle si une valeur constante est utilisée pour le nombre de bitsJe suis sûr que c'est la méthode générale la plus rapide que vous trouverez)
J'ai écrit une macro de calcul du nombre de bits rapide pour les machines RISC vers 1990. Il n'utilise pas l'arithmétique avancée (multiplication, division,%), les extractions de mémoire (beaucoup trop lentes), les branches (beaucoup trop lentes), mais le processeur a Le baril-décaleur 32 bits (en d'autres termes, les groupes >> 1 et >> 32 prennent le même nombre de cycles.) Cela suppose que de petites constantes (telles que 6, 12, 24) ne coûtent rien à charger dans les registres ou sont stockées. dans les temporaires et réutilisé encore et encore.
Avec ces hypothèses, il compte 32 bits en environ 16 cycles/instructions sur la plupart des machines RISC. Notez que 15 instructions/cycles est proche d'une limite inférieure du nombre de cycles ou d'instructions, car il semble prendre au moins 3 instructions (masque, décalage, opérateur) pour réduire de moitié le nombre d'addend, donc log_2 (32) = 5, 5 x 3 = 15 instructions est une quasi-limite inférieure.
#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;
Voici un secret pour la première et la plus complexe des étapes:
input output
AB CD Note
00 00 = AB
01 01 = AB
10 01 = AB - (A >> 1) & 0x1
11 10 = AB - (A >> 1) & 0x1
donc, si je prends la 1ère colonne (A) ci-dessus, le décale de 1 bit vers la droite et le soustrait de AB, je reçois la sortie (CD). L'extension à 3 bits est similaire; vous pouvez le vérifier avec une table booléenne à 8 lignes comme la mienne ci-dessus si vous le souhaitez.
J'utilise toujours cela dans la programmation compétitive et il est facile à écrire et efficace:
#include <bits/stdc++.h>
using namespace std;
int countOnes(int n) {
bitset<32> b(n);
return b.count();
}
J'ai trouvé une implémentation du comptage de bits dans un tableau avec l'utilisation de l'instruction SIMD (SSSE3 et AVX2). Ses performances sont 2 à 2,5 fois supérieures à celles de la fonction intrinsèque __popcnt64.
Version 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];
}
Version 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];
}
J'aime particulièrement cet exemple du fichier fortune:
# define BITCOUNT (x) (((BX_ (x) + (BX_ (x) >> 4)) & 0x0F0F0F0F) 255: x) >> 1) & 0x77777777) - ((x) >> 2) & 0x33333333) - ((x) >> 3) & 0x11111111)).
J'aime mieux parce que c'est tellement joli!
Java JDK1.5
Integer.bitCount (n);
où n est le nombre dont les 1 doivent être comptés.
vérifier aussi,
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);
}
Voici un module portable (ANSI-C) qui peut analyser chacun de vos algorithmes sur n'importe quelle architecture.
Votre CPU a 9 octets bits? Pas de problème :-) Pour le moment, il implémente 2 algorithmes, l'algorithme K & R et une table de recherche par octets. La table de correspondance est en moyenne 3 fois plus rapide que l'algorithme K & R. Si quelqu'un peut trouver un moyen de rendre portable l'algorithme "Hacker's Delight", n'hésitez pas à l'ajouter.
#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
Il existe de nombreux algorithmes pour compter les bits définis. mais je pense que le meilleur est le plus rapide! Vous pouvez voir le détail sur cette page:
Je suggère celui-ci:
Bits de comptage définis dans des mots de 14, 24 ou 32 bits à l'aide d'instructions de 64 bits
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;
Cette méthode nécessite un processeur 64 bits à division de module rapide pour être efficace. La première option ne prend que 3 opérations; la deuxième option prend 10; et la troisième option prend 15.
Solution rapide en C # utilisant un tableau précalculé de comptes de bits en octets avec branchement sur la taille d'entré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
};
}
32 bits ou pas? Je viens d’arriver avec cette méthode en Java après avoir lu " craquer l’interview de codage " 4ème édition exercice 5.5 (chapitre 5: Manipulation des bits). Si le bit le moins significatif est égal à 1 incrémentation count
, déplacez le nombre entier à droite.
public static int bitCount( int n){
int count = 0;
for (int i=n; i!=0; i = i >> 1){
count += i & 1;
}
return count;
}
Je pense que celle-ci est plus intuitive que les solutions à constante 0x33333333, quelle que soit leur rapidité. Cela dépend de votre définition du "meilleur algorithme".
ce que tu peux faire c'est
while(n){
n=n&(n-1);
count++;
}
la logique derrière cela est que les bits de n-1 sont inversés à partir du bit de réglage le plus à droite de n . si n = 6, soit 110 , alors 5 à 101, les bits sont inversés du bit de réglage le plus à droite de n . Donc, si nous et ces deux-là, nous ferons le bit le plus à droite 0 à chaque itération et passerons toujours au bit le plus à droite suivant. Par conséquent, en comptant le bit défini, la pire complexité temporelle sera O(logn) lorsque chaque bit est réglé.
Personnellement j'utilise ceci:
public static int myBitCount(long L){
int count = 0;
while (L != 0) {
count++;
L ^= L & -L;
}
return count;
}
int bitcount(unsigned int n)
{
int count=0;
while(n)
{
count += n & 0x1u;
n >>= 1;
}
return count;
}
Le «compte» itéré s'exécute dans le temps proportionnellement au nombre total de bits. Il parcourt simplement tous les bits, se terminant un peu plus tôt en raison de la condition while. Utile si l’S ou les bits définis sont rares et parmi les bits de poids faible .
Un autre algorithme de pondération de Hamming si vous utilisez un processeur compatible BMI2
the_weight=__tzcnt_u64(~_pext_u64(data[i],data[i]));
S'amuser!
Vous pouvez utiliser la fonction intégrée nommée __builtin_popcount (). Il n'y a pas __builtin_popcount en C++, mais il s'agit d'une fonction intégrée du compilateur GCC. Cette fonction renvoie le nombre de bits définis dans un entier.
int __builtin_popcount (unsigned int x);
Référence: Bit Twiddling Hacks
int countBits(int x)
{
int n = 0;
if (x) do n++;
while(x=x&(x-1));
return n;
}
Ou aussi:
int countBits(int x) { return (x)? 1+countBits(x&(x-1)): 0; }
En Java 8 ou 9, appelez simplement Integer.bitCount
.
Voici une solution qui n’a pas été mentionnée jusqu’à présent, utilisant des champs de bits. Le programme suivant compte les bits définis dans un tableau de 100000000 nombres entiers de 16 bits en utilisant 4 méthodes différentes. Les résultats de chronométrage sont donnés entre parenthèses (sous MacOSX, avec 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;
}
Bien que la version de champ de bits soit très lisible, les résultats du minutage montrent qu’elle est 4x plus lente que NumberOfSetBits()
. Les implémentations basées sur des tables de consultation sont encore un peu plus rapides, en particulier avec une table de 65 ko.
Voici l'exemple de code qui pourrait être utile.
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
La proposition suivante a été fusionnée http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2019/p0553r4.html et devrait l'ajouter à la <bit>
entête.
Je m'attends à ce que l'utilisation soit comme:
#include <bit>
#include <iostream>
int main() {
std::cout << std::popcount(0x55) << std::endl;
}
Je vais essayer quand le support arrive à GCC, GCC 9.1.0 avec g++-9 -std=c++2a
ne le supporte toujours pas.
La proposition dit:
En-tête:
<bit>
namespace std { // 25.5.6, counting template<class T> constexpr int popcount(T x) noexcept;
et:
template<class T> constexpr int popcount(T x) noexcept;
Contraintes: T est un type entier non signé (3.9.1 [basic.fundamental]).
Retourne: Le nombre de 1 bits dans la valeur de x.
std::rotl
et std::rotr
ont également été ajoutés pour effectuer des rotations de bits circulaires: Meilleures pratiques pour les opérations de décalage circulaire (rotation) en 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
Je n'ai vu cette approche nulle part:
int nbits(unsigned char v) {
return ((((v - ((v >> 1) & 0x55)) * 0x1010101) & 0x30c00c03) * 0x10040041) >> 0x1c;
}
Cela fonctionne par octet, il devrait donc être appelé 4 fois pour un entier 32 bits. Il est dérivé de l'addition latérale mais utilise deux multiplications 32 bits pour réduire le nombre d'instructions à 7.
La plupart des compilateurs C optimisent cette fonction à l'aide d'instructions SIMD (SSE2) lorsqu'il est clair que le nombre de demandes est un multiple de 4 et qu'il devient très compétitif. Il est portable, peut être défini comme une macro ou une fonction en ligne et n’a pas besoin de tables de données.
Cette approche peut être étendue pour fonctionner sur 16 bits à la fois, en utilisant des multiplications à 64 bits. Cependant, il échoue lorsque tous les 16 bits sont définis, renvoyant zéro. Il ne peut donc être utilisé que lorsque la valeur d'entrée 0xffff n'est pas présente. Il est également plus lent en raison des opérations 64 bits et n’optimise pas bien.
Voici quelque chose qui fonctionne dans PHP (tous les entiers PHP sont signés 32 bits, ce 31 bits):
function bits_population($nInteger)
{
$nPop=0;
while($nInteger)
{
$nInteger^=(1<<(floor(1+log($nInteger)/log(2))-1));
$nPop++;
}
return $nPop;
}
Que diriez-vous de convertir l'entier en chaîne binaire et de compter ceux-ci?
solution php:
substr_count( decbin($integer), '1' );
Algorithme simple pour compter le nombre de bits définis:
int countbits(n){
int count = 0;
while(n != 0){
n = n & (n-1);
count++;
}
return count;
}
Prenons l'exemple de 11 (1011) et essayons de parcourir manuellement l'algorithme. Devrait vous aider beaucoup!
Un moyen simple, qui devrait bien fonctionner pour une petite quantité de bits, ressemble à ceci (pour 4 bits dans cet exemple):
(i & 1) + (i & 2)/2 + (i & 4)/4 + (i & 8)/8
Est-ce que d'autres recommandent ceci pour un petit nombre de bits comme solution simple?