Poids de marteau - Hamming weight

Le poids de Hamming d'une chaîne est le nombre de symboles qui sont différents du symbole zéro de l' alphabet utilisé. Elle est donc équivalente à la distance de Hamming à partir de la chaîne entièrement nulle de même longueur. Pour le cas le plus typique, une chaîne de bits , c'est le nombre de 1 dans la chaîne, ou la somme des chiffres de la représentation binaire d'un nombre donné et de la norme d'un vecteur de bits. Dans ce cas binaire, il est également appelé population count , popcount , sideways sum ou bit summation .

Exemples
Chaîne de caractères Poids de marteau
111 0 1 4
111 0 1 000 4
00000000 0
678 0 1234 0 567 dix
Un graphique pour le dénombrement de la population (poids de Hamming pour les nombres binaires) pour les nombres (décimaux) de 0 à 256.

Historique et utilisation

Le poids de Hamming est nommé d'après Richard Hamming bien qu'il n'ait pas été à l'origine de la notion. Le poids de Hamming des nombres binaires était déjà utilisé en 1899 par James WL Glaisher pour donner une formule pour le nombre de coefficients binomiaux impairs dans une seule rangée du triangle de Pascal . Irving S. Reed a introduit un concept, équivalent au poids de Hamming dans le cas binaire, en 1954.

Poids Hamming est utilisé dans plusieurs disciplines , y compris la théorie de l' information , la théorie du codage et la cryptographie . Exemples d'applications du poids Hamming :

  • Dans l' exponentiation modulaire par élévation au carré , le nombre de multiplications modulaires requises pour un exposant e est log 2 e + poids( e ). C'est la raison pour laquelle la valeur de clé publique e utilisée dans RSA est généralement choisie pour être un nombre de faible poids de Hamming.
  • Le poids de Hamming détermine les longueurs de chemin entre les nœuds dans les tables de hachage distribuées Chord .
  • Les recherches IrisCode dans les bases de données biométriques sont généralement implémentées en calculant la distance de Hamming à chaque enregistrement stocké.
  • Dans les programmes d' échecs informatiques utilisant une représentation bitboard , le poids de Hamming d'un bitboard donne le nombre de pièces d'un type donné restant dans le jeu, ou le nombre de cases de l'échiquier contrôlées par les pièces d'un joueur, et est donc un terme important. à la valeur d'un poste.
  • Le poids de Hamming peut être utilisé pour calculer efficacement le premier ensemble trouvé en utilisant l'identité ffs(x) = pop(x ^ (x - 1)). Ceci est utile sur les plates-formes telles que SPARC qui ont des instructions matérielles de poids Hamming mais aucun matériel ne trouve les premières instructions définies.
  • L'opération de poids de Hamming peut être interprétée comme une conversion du système de numération unaire en nombres binaires .
  • Dans la mise en œuvre de certaines structures de données succinctes comme les vecteurs de bits et les arbres d'ondelettes .

Mise en œuvre efficace

Le nombre de population d'une chaîne de bits est souvent nécessaire dans la cryptographie et d'autres applications. La distance de Hamming de deux mots A et B peut être calculée comme le poids de Hamming de A x ou B .

Le problème de sa mise en œuvre efficace a été largement étudié. Une seule opération pour le calcul, ou des opérations parallèles sur des vecteurs de bits sont disponibles sur certains processeurs . Pour les processeurs dépourvus de ces fonctionnalités, les meilleures solutions connues sont basées sur l'ajout de comptes dans un modèle d'arbre. Par exemple, pour compter le nombre de 1 bits dans le nombre binaire de 16 bits a = 0110 1100 1011 1010, ces opérations peuvent être effectuées :

Expression Binaire Décimal Commenter
a 01 dix 11 00 dix 11 dix dix 27834 Le numéro d'origine
b0 = (a >> 0) & 01 01 01 01 01 01 01 01 01 00 01 00 00 01 00 00 1, 0, 1, 0, 0, 1, 0, 0 Chaque autre bit d'un
b1 = (a >> 1) & 01 01 01 01 01 01 01 01 00 01 01 00 01 01 01 01 0, 1, 1, 0, 1, 1, 1, 1 Les bits restants d'un
c = b0 + b1 01 01 dix 00 01 dix 01 01 1, 1, 2, 0, 1, 2, 1, 1 Nombre de 1 dans chaque tranche de 2 bits d'un
d0 = (c >> 0) & 0011 0011 0011 0011 0001 0000 0010 0001 1, 0, 2, 1 Chaque autre compte à partir de c
d2 = (c >> 2) & 0011 0011 0011 0011 0001 0010 0001 0001 1, 2, 1, 1 Le reste compte de c
e = d0 + d2 0010 0010 0011 0010 2, 2, 3, 2 Nombre de 1 dans chaque tranche de 4 bits d'un
f0 = (e >> 0) & 00001111 00001111 0000010 0000010 2, 2 Chaque autre compte à partir de e
f4 = (e >> 4) & 00001111 00001111 0000010 00000011 2, 3 Le reste compte de e
g = f0 + f4 00000100 00000101 4, 5 Nombre de 1 dans chaque tranche de 8 bits d'un
h0 = (g >> 0) & 0000000011111111 000000000000101 5 Chaque autre compte à partir de g
h8 = (g >> 8) & 0000000011111111 000000000000100 4 Le reste compte de g
i = h0 + h8 0000000000001001 9 Nombre de 1 dans tout le mot de 16 bits

Ici, les opérations sont comme dans le langage de programmation C , donc cela X >> Ysignifie décaler X vers la droite de Y bits, X & Y signifie le ET au niveau du bit de X et Y, et + est une addition ordinaire. Les meilleurs algorithmes connus pour ce problème sont basés sur le concept illustré ci-dessus et sont donnés ici :

//types and constants used in the functions below
//uint64_t is an unsigned 64-bit integer variable type (defined in C99 version of C language)
const uint64_t m1  = 0x5555555555555555; //binary: 0101...
const uint64_t m2  = 0x3333333333333333; //binary: 00110011..
const uint64_t m4  = 0x0f0f0f0f0f0f0f0f; //binary:  4 zeros,  4 ones ...
const uint64_t m8  = 0x00ff00ff00ff00ff; //binary:  8 zeros,  8 ones ...
const uint64_t m16 = 0x0000ffff0000ffff; //binary: 16 zeros, 16 ones ...
const uint64_t m32 = 0x00000000ffffffff; //binary: 32 zeros, 32 ones
const uint64_t h01 = 0x0101010101010101; //the sum of 256 to the power of 0,1,2,3...

//This is a naive implementation, shown for comparison,
//and to help in understanding the better functions.
//This algorithm uses 24 arithmetic operations (shift, add, and).
int popcount64a(uint64_t x)
{
    x = (x & m1 ) + ((x >>  1) & m1 ); //put count of each  2 bits into those  2 bits 
    x = (x & m2 ) + ((x >>  2) & m2 ); //put count of each  4 bits into those  4 bits 
    x = (x & m4 ) + ((x >>  4) & m4 ); //put count of each  8 bits into those  8 bits 
    x = (x & m8 ) + ((x >>  8) & m8 ); //put count of each 16 bits into those 16 bits 
    x = (x & m16) + ((x >> 16) & m16); //put count of each 32 bits into those 32 bits 
    x = (x & m32) + ((x >> 32) & m32); //put count of each 64 bits into those 64 bits 
    return x;
}

//This uses fewer arithmetic operations than any other known  
//implementation on machines with slow multiplication.
//This algorithm uses 17 arithmetic operations.
int popcount64b(uint64_t x)
{
    x -= (x >> 1) & m1;             //put count of each 2 bits into those 2 bits
    x = (x & m2) + ((x >> 2) & m2); //put count of each 4 bits into those 4 bits 
    x = (x + (x >> 4)) & m4;        //put count of each 8 bits into those 8 bits 
    x += x >>  8;  //put count of each 16 bits into their lowest 8 bits
    x += x >> 16;  //put count of each 32 bits into their lowest 8 bits
    x += x >> 32;  //put count of each 64 bits into their lowest 8 bits
    return x & 0x7f;
}

//This uses fewer arithmetic operations than any other known  
//implementation on machines with fast multiplication.
//This algorithm uses 12 arithmetic operations, one of which is a multiply.
int popcount64c(uint64_t x)
{
    x -= (x >> 1) & m1;             //put count of each 2 bits into those 2 bits
    x = (x & m2) + ((x >> 2) & m2); //put count of each 4 bits into those 4 bits 
    x = (x + (x >> 4)) & m4;        //put count of each 8 bits into those 8 bits 
    return (x * h01) >> 56;  //returns left 8 bits of x + (x<<8) + (x<<16) + (x<<24) + ... 
}

Les implémentations ci-dessus ont le meilleur comportement dans le pire des cas de tous les algorithmes connus. Cependant, lorsqu'une valeur est censée avoir quelques bits différents de zéro, il peut être plus efficace d'utiliser des algorithmes qui comptent ces bits un par un. Comme Wegner l'a décrit en 1960, l' ET au niveau du bit de x avec x  − 1 ne diffère de x que par la mise à zéro du bit non nul le moins significatif : la soustraction de 1 change la chaîne la plus à droite de 0 à 1 et le 1 le plus à droite à 0. Si x avait à l'origine n bits qui étaient 1, puis après seulement n itérations de cette opération, x sera réduit à zéro. L'implémentation suivante est basée sur ce principe.

//This is better when most bits in x are 0
//This algorithm works the same for all data sizes.
//This algorithm uses 3 arithmetic operations and 1 comparison/branch per "1" bit in x.
int popcount64d(uint64_t x)
{
    int count;
    for (count=0; x; count++)
        x &= x - 1;
    return count;
}

Si une plus grande utilisation de la mémoire est autorisée, nous pouvons calculer le poids de Hamming plus rapidement que les méthodes ci-dessus. Avec une mémoire illimitée, nous pourrions simplement créer une grande table de recherche du poids de Hamming de chaque entier de 64 bits. Si nous pouvons stocker une table de recherche de la fonction de Hamming de chaque entier de 16 bits, nous pouvons procéder comme suit pour calculer le poids de Hamming de chaque entier de 32 bits.

static uint8_t wordbits[65536] = { /* bitcounts of integers 0 through 65535, inclusive */ };
//This algorithm uses 3 arithmetic operations and 2 memory reads.
int popcount32e(uint32_t x)
{
    return wordbits[x & 0xFFFF] + wordbits[x >> 16];
}
//Optionally, the wordbits[] table could be filled using this function
int popcount32e_init(void)
{
    uint32_t i;
    uint16_t x;
    int count;
    for (i=0; i <= 0xFFFF; i++)
    {
        x = i;
        for (count=0; x; count++) // borrowed from popcount64d() above
            x &= x - 1;
        wordbits[i] = count;
    }
}

Muła et al. ont montré qu'une version vectorisée de popcount64b peut s'exécuter plus rapidement que des instructions dédiées (par exemple, popcnt sur les processeurs x64).

Poids minimum

Dans le codage correcteur d'erreurs , le poids de Hamming minimum, communément appelé poids minimum w min d'un code, est le poids du mot de code non nul de poids le plus faible. Le poids w d'un mot de code est le nombre de 1 dans le mot. Par exemple, le mot 11001010 a un poids de 4.

Dans un code de bloc linéaire, le poids minimum est également la distance de Hamming minimum ( d min ) et définit la capacité de correction d'erreur du code. Si w min  =  n , alors d min  =  n et le code corrigera jusqu'à d min /2 erreurs.

Support linguistique

Certains compilateurs C fournissent des fonctions intrinsèques qui fournissent des fonctionnalités de comptage de bits. Par exemple, GCC (depuis la version 3.4 en avril 2004) inclut une fonction intégrée __builtin_popcountqui utilisera une instruction de processeur si disponible ou une implémentation de bibliothèque efficace dans le cas contraire. LLVM-GCC a inclus cette fonction depuis la version 1.5 en juin 2005.

Dans C++ STL , la structure de données de tableau de bits bitseta une count()méthode qui compte le nombre de bits définis. En C++20 , un nouvel en-tête a <bit>été ajouté, contenant des fonctions std::popcountet std::has_single_bit, prenant des arguments de types entiers non signés.

En Java, la structure de données de tableau de bits extensible BitSeta une BitSet.cardinality()méthode qui compte le nombre de bits définis. De plus, il existe des fonctions Integer.bitCount(int)et Long.bitCount(long)pour compter les bits dans les entiers primitifs 32 bits et 64 bits, respectivement. En outre, la BigIntegerclasse d'entiers de précision arbitraire possède également une BigInteger.bitCount()méthode qui compte les bits.

En Python , le inttype a une bit_count()méthode pour compter le nombre de bits définis. Cette fonctionnalité est nouvelle dans Python 3.10, dont la sortie est prévue en 2021.

En Common Lisp , la fonction logcount, étant donné un entier non négatif, renvoie le nombre de 1 bits. (Pour les entiers négatifs, il renvoie le nombre de 0 bits en notation complémentaire à 2.) Dans les deux cas, l'entier peut être un BIGNUM.

À partir de GHC 7.4, le package de base Haskell a une popCountfonction disponible sur tous les types qui sont des instances de la Bitsclasse (disponible à partir du Data.Bitsmodule).

La version MySQL du langage SQL fournit BIT_COUNT()une fonction standard.

Fortran 2008 a la fonction élémentaire intrinsèque standard popcntrenvoyant le nombre de bits non nuls dans un entier (ou un tableau d'entiers).

Certaines calculatrices de poche scientifiques programmables disposent de commandes spéciales pour calculer le nombre de bits définis, par exemple #Bsur le HP-16C et le WP 43S , #BITSou BITSUMsur les émulateurs HP-16C, et nBITSsur le WP 34S .

FreePascal implémente popcnt depuis la version 3.0.

Prise en charge du processeur

Voir également

Les références

Lectures complémentaires

Liens externes