84 votes

Compter le nombre de 1 dans la représentation binaire

Une façon efficace de compter le nombre de 1 dans la représentation binaire d'un nombre en O(1) si vous avez assez de mémoire pour jouer avec. Il s'agit d'une question d'entretien que j'ai trouvée sur un forum en ligne, mais qui n'avait pas de réponse. Quelqu'un peut-il suggérer quelque chose, je ne vois pas comment le faire en O(1) temps ?

2 votes

La question veut donc que vous trichiez - une mémoire "suffisante" pourrait facilement être constituée de plus de bits qu'il n'y a d'atomes dans l'univers observable.

0 votes

Ne serait-il pas simplement un tableau de longueur MAX_INT ?

1 votes

Corrigez-moi si je me trompe - si nous avons un tableau [0..MAX_INT-1] où l'index est le nombre réel en entrée, et les données sont le nombre de 1 pour ce nombre (et disons qu'il est mis en œuvre via une mémoire adressable par le contenu), nous pourrions dire que nous avons un tableau [0..MAX_INT-1]. fr.wikipedia.org/wiki/Content-addressable_memory ) ne serait-elle pas O(1) ?

61voto

Óscar López Points 97105

C'est le poids de Hamming alias le comptage de la population. Le lien mentionne des mises en œuvre efficaces. Citation :

Avec une mémoire illimitée, nous pourrions simplement créer une grande table de consultation du poids de Hamming de chaque entier de 64 bits.

8 votes

+1 pour la dénomination correcte de ce problème. Bien que je pense qu'une réponse complète indiquerait que même l'utilisation d'une table de consultation ne serait pas un temps O(1), car le temps de consultation d'une entrée dépend de la taille de l'entrée.

0 votes

@TimGee Un accès à une LUT est toujours considéré comme étant O(1). Lorsque vous avez une mémoire illimitée, vous avez également un bus d'adresse illimité. Ainsi, quelle que soit l'adresse (= entrée) à laquelle vous accédez, il s'agit toujours d'un seul accès à la mémoire ;-)

0 votes

@Scolytus, des références pour "Quand on a une mémoire illimitée, on a aussi un bus d'adresse illimité" ?

55voto

0605002 Points 5702

J'ai une solution qui compte les bits dans O(Number of 1's) temps :

bitcount(n):
    count = 0
    while n > 0:
        count = count + 1
        n = n & (n-1)
    return count

Dans le pire des cas (lorsque le nombre est 2^n - 1, tous les 1 en binaire), il vérifiera chaque bit.

Edit : Je viens de trouver un très bel algorithme à temps constant et à mémoire constante pour le comptage des bits. Le voici, écrit en C :

int BitCount(unsigned int u)
{
     unsigned int uCount;

     uCount = u - ((u >> 1) & 033333333333) - ((u >> 2) & 011111111111);
     return ((uCount + (uCount >> 3)) & 030707070707) % 63;
}

Vous pouvez trouver la preuve de son exactitude ici .

0 votes

L'idée de la question est d'utiliser le temps plat, c'est-à-dire qu'il n'y a pas de différence dans le temps d'exécution qu'il y ait zéro, un ou plusieurs 1.

1 votes

Le deuxième n'est en temps constant que parce que la taille de l'entrée est constante, et dans ce cas, le premier l'est aussi. Le premier algorithme est en fait O(log n) dans le cas général, car les opérations ET et soustraction au sens du bit prennent autant de temps.

4 votes

Votre première réponse est O(log n) et non O(1). Votre deuxième réponse est O(1) mais suppose un domaine de 32 bits (le paramètre d'entrée est unsigned int ).

20voto

Sriram Mahavadi Points 56

Veuillez noter que : n&(n-1) élimine toujours le 1 le moins significatif.

Nous pouvons donc écrire le code pour calculer le nombre de 1 de la manière suivante :

count=0;
while(n!=0){
  n = n&(n-1);
  count++;
}
cout<<"Number of 1's in n is: "<<count;

La complexité du programme serait : nombre de 1 dans n (qui est constamment < 32).

16voto

user2555279 Points 51

J'ai vu la solution suivante sur un autre site web :

int count_one(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;
}

11voto

akus Points 79
public static void main(String[] args) {

    int a = 3;
    int orig = a;
    int count = 0;
    while(a>0)
    {
        a = a >> 1 << 1;
        if(orig-a==1)
            count++;
        orig = a >> 1;
        a = orig;
    }

    System.out.println("Number of 1s are: "+count);
}

4 votes

+1 Bonne réponse. Essayez d'ajouter quelques explications. Surtout pour les parties de décalage de bits dans les deux sens, ce qui peut être déroutant pour certains.

2 votes

Explication : A) Si le bit le moins significatif est 1, augmenter le compteur de 1 (voici comment cette partie fonctionne : le décalage vers la droite, puis l'annulation du décalage met le bit le moins significatif à 0... si la différence entre l'ancien et le nouveau nombre est de 1, alors un 1 a été enlevé). B) Divisez le nombre par 2 et prenez le plancher en décalant de 1 vers la droite C) Répétez jusqu'à ce que le nombre soit 0. Il serait plus facile de simplement faire un ET entre le nombre et 1 pour déterminer si le chiffre le moins significatif est un 1.

3 votes

Bonjour, je suis juste curieuse, est-ce qu'une if (orig & 1) count++; orig >>= 1; être un peu plus efficace ?

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X