web-dev-qa-db-fra.com

Comment vérifier si un nombre est une puissance de 2

Aujourd'hui, j'avais besoin d'un algorithme simple pour vérifier si un nombre est une puissance de 2.

L'algorithme doit être:

  1. Facile
  2. Corrigez pour toute valeur de ulong.

Je suis venu avec cet algorithme simple:

private bool IsPowerOfTwo(ulong number)
{
    if (number == 0)
        return false;

    for (ulong power = 1; power > 0; power = power << 1)
    {
        // This for loop used shifting for powers of 2, meaning
        // that the value will become 0 after the last shift
        // (from binary 1000...0000 to 0000...0000) then, the 'for'
        // loop will break out.

        if (power == number)
            return true;
        if (power > number)
            return false;
    }
    return false;
}

Mais ensuite, je me suis demandé si je vérifierais si log2 x est un nombre tout à fait rond. Mais lorsque j'ai vérifié 2 ^ 63 + 1, Math.Log est retourné exactement 63 en raison de l'arrondissement. J'ai donc vérifié si 2 à la puissance 63 est égal au nombre d'origine - et c'est le cas, car le calcul est effectué dans doubles et non pas en chiffres exacts:

private bool IsPowerOfTwo_2(ulong number)
{
    double log = Math.Log(number, 2);
    double pow = Math.Pow(2, Math.Round(log));
    return pow == number;
}

Ceci a retourné true pour la valeur incorrecte donnée: 9223372036854775809.

Y a-t-il un meilleur algorithme?

541
configurator

Il y a une astuce simple pour ce problème:

bool IsPowerOfTwo(ulong x)
{
    return (x & (x - 1)) == 0;
}

Notez que cette fonction signalera true pour 0, qui n'est pas une puissance de 2. Si vous voulez exclure cela, voici comment:

bool IsPowerOfTwo(ulong x)
{
    return (x != 0) && ((x & (x - 1)) == 0);
}

Explication

Tout d’abord le binaire et l’opérateur au niveau du bit de la définition MSDN:

Les opérateurs binaires et sont prédéfinis pour les types intégraux et bool. Pour les types intégraux, & calcule le ET logique au niveau des bits de ses opérandes. Pour les opérandes bool, & calcule le ET logique de ses opérandes; c'est-à-dire que le résultat est vrai si et seulement si ses deux opérandes sont vrais.

Voyons maintenant comment cela se passe:

La fonction retourne une valeur booléenne (true/false) et accepte un paramètre entrant de type unsigned long (x, dans ce cas). Supposons, par souci de simplicité, que quelqu'un ait passé la valeur 4 et appelle la fonction ainsi:

bool b = IsPowerOfTwo(4)

Nous remplaçons maintenant chaque occurrence de x par 4:

return (4 != 0) && ((4 & (4-1)) == 0);

Eh bien, nous savons déjà que 4! = 0 évaluations sont vraies, jusqu’ici tout va bien. Mais qu'en est-il:

((4 & (4-1)) == 0)

Cela se traduit bien sûr par ceci:

((4 & 3) == 0)

Mais en quoi consiste exactement 4&3?

La représentation binaire de 4 est 100 et la représentation binaire de 3 est 011 (rappelez-vous que & prend la représentation binaire de ces nombres). Nous avons donc:

100 = 4
011 = 3

Imaginez que ces valeurs s’empilent un peu comme une addition élémentaire. L'opérateur & indique que si les deux valeurs sont égales à 1, le résultat est 1, sinon il est 0. Donc 1 & 1 = 1, 1 & 0 = 0, 0 & 0 = 0 et 0 & 1 = 0. Alors on fait le calcul:

100
011
----
000

Le résultat est simplement 0. Nous allons donc revenir en arrière et regarder ce que notre déclaration de retour traduit maintenant par:

return (4 != 0) && ((4 & 3) == 0);

Ce qui se traduit maintenant par:

return true && (0 == 0);
return true && true;

Nous savons tous que true && true est simplement true, et cela montre que pour notre exemple, 4 est une puissance de 2.

1143
Greg Hewgill

Certains sites qui documentent et expliquent ceci et d'autres bidouilles sont:

Et leur grand-mère le livre "Hacker's Delight" de Henry Warren, Jr. :

Comme la page de Sean Anderson explique, l'expression ((x & (x - 1)) == 0) indique incorrectement que 0 est une puissance de 2. Il suggère d'utiliser:

(!(x & (x - 1)) && x)

pour corriger ce problème.

95
Michael Burr

return (i & -i) == i

39
Andreas Petersson
bool IsPowerOfTwo(ulong x)
{
    return x > 0 && (x & (x - 1)) == 0;
}
22
Matt Howells

J'ai récemment écrit un article à ce sujet sur http://www.exploringbinary.com/ten-ways-to-check-if-an-integer-is-a-power-of-two-in-c/ . Il couvre le comptage de bits, comment utiliser les logarithmes correctement, le contrôle classique "x &&! (X & (x - 1))", et autres.

20
Rick Regan

Voici une solution simple C++ :

bool IsPowerOfTwo( unsigned int i )
{
    return std::bitset<32>(i).count() == 1;
}
16
deft_code
    bool IsPowerOfTwo(int n)
    {
        if (n > 1)
        {
            while (n%2 == 0)
            {
                n >>= 1;
            }
        }
        return n == 1;
    }

Et voici un algorithme général pour déterminer si un nombre est une puissance d'un autre nombre.

    bool IsPowerOf(int n,int b)
    {
        if (n > 1)
        {
            while (n % b == 0)
            {
                n /= b;
            }
        }
        return n == 1;
    }
10
Raz Megrelidze

Le complément suivant à la réponse acceptée peut être utile à certaines personnes:

Une puissance de deux, exprimée en binaire, ressemblera toujours à 1 suivie de n zéros où n est supérieur ou égal à 0. Ex:

Decimal  Binary
1        1     (1 followed by 0 zero)
2        10    (1 followed by 1 zero)
4        100   (1 followed by 2 zeroes)
8        1000  (1 followed by 3 zeroes)
.        .
.        .
.        .

etc.

Lorsque nous soustrayons 1 de ce type de nombres, ils deviennent 0 suivis de n uns et n est identique à ci-dessus. Ex:

Decimal    Binary
1 - 1 = 0  0    (0 followed by 0 one)
2 - 1 = 1  01   (0 followed by 1 one)
4 - 1 = 3  011  (0 followed by 2 ones)
8 - 1 = 7  0111 (0 followed by 3 ones)
.          .
.          .
.          .

etc.

Venir au coeur

Que se passe-t-il lorsque nous faisons un bit AND au niveau d'un nombre x, qui est une puissance de 2, et x - 1?

Celui de x s'aligne sur le zéro de x - 1 et tous les zéros de x s'aligne sur ceux de x - 1, ce qui entraîne le bitwise AND pour donner 0. Et c’est comme cela que la réponse à une seule ligne mentionnée ci-dessus est exacte.


Ajoutant encore à la beauté de la réponse acceptée ci-dessus -

Nous avons donc une propriété à notre disposition maintenant:

Lorsque nous soustrayons 1 à n’importe quel nombre, dans la représentation binaire, le plus à droite 1 deviendra 0 et tous les zéros précédant celui-ci deviendra maintenant 1

Une utilisation géniale de cette propriété est de découvrir - Combien de 1 sont présents dans la représentation binaire d'un nombre donné? Le code court et simple permettant de le faire pour un entier donné x est :

byte count = 0;
for ( ; x != 0; x &= (x - 1)) count++;
Console.Write("Total ones in the binary representation of x = {0}", count);

Un autre aspect des nombres qui peut être prouvé à partir du concept expliqué ci-dessus est "Tout nombre positif peut-il être représenté par la somme des puissances de 2?".

Oui, chaque nombre positif peut être représenté par la somme des puissances de 2. Pour tout nombre, prenons sa représentation binaire. Ex: Numéro de prise 117.

The binary representation of 117 is 1110101

Because  1110101 = 1000000 + 100000 + 10000 + 0000 + 100 + 00 + 1
we have  117     = 64      + 32     + 16    + 0    + 4   + 0  + 1
10
displayName

Après avoir posté la question, j'ai pensé à la solution suivante:

Nous devons vérifier si l'un des chiffres binaires est exactement un. Donc, nous déplaçons simplement le nombre d'un chiffre à l'autre et retournons true s'il est égal à 1. Si nous venons d'un nombre impair ((number & 1) == 1), nous savons que le résultat est false. Cela s'est avéré (à l'aide d'un repère) légèrement plus rapide que la méthode d'origine pour les valeurs (grandes) vraies et beaucoup plus rapide pour les valeurs fausses ou petites.

private static bool IsPowerOfTwo(ulong number)
{
    while (number != 0)
    {
        if (number == 1)
            return true;

        if ((number & 1) == 1)
            // number is an odd number and not 1 - so it's not a power of two.
            return false;

        number = number >> 1;
    }
    return false;
}

Bien sûr, la solution de Greg est bien meilleure.

10
configurator
bool isPow2 = ((x & ~(x-1))==x)? !!x : 0;
6
abelenky
return ((x != 0) && !(x & (x - 1)));

Si x est une puissance de deux, son seul bit 1 est en position n. Cela signifie que x – 1 a un 0 en position n. Pour voir pourquoi, rappelez-vous comment fonctionne une soustraction binaire. En soustrayant 1 de x, l’emprunt se propage jusqu’à la position n; le bit n devient 0 et tous les bits inférieurs deviennent 1. Maintenant, puisque x n'a pas de bit 1 en commun avec x – 1, x & (x – 1) vaut 0 et !(x & (x – 1)) est vrai.

4
Prakash Jat
int isPowerOfTwo(unsigned int x)
{
    return ((x != 0) && ((x & (~x + 1)) == x));
}

C'est vraiment rapide. Il faut environ 6 minutes et 43 secondes pour vérifier tous les 2 ^ 32 entiers.

4
sudeepdino008
bool isPowerOfTwo(int x_)
{
  register int bitpos, bitpos2;
  asm ("bsrl %1,%0": "+r" (bitpos):"rm" (x_));
  asm ("bsfl %1,%0": "+r" (bitpos2):"rm" (x_));
  return bitpos > 0 && bitpos == bitpos2;
}
4
bugs king

Trouvez si le nombre donné est une puissance de 2.

#include <math.h>

int main(void)
{
    int n,logval,powval;
    printf("Enter a number to find whether it is s power of 2\n");
    scanf("%d",&n);
    logval=log(n)/log(2);
    powval=pow(2,logval);

    if(powval==n)
        printf("The number is a power of 2");
    else
        printf("The number is not a power of 2");

    getch();
    return 0;
}
4
udhaya

Voici une autre méthode que j'ai conçue, dans ce cas, en utilisant | au lieu de &:

bool is_power_of_2(ulong x) {
    if(x ==  (1 << (sizeof(ulong)*8 -1) ) return true;
    return (x > 0) && (x<<1 == (x|(x-1)) +1));
}
3
Chethan

Un nombre est une puissance de 2 s'il ne contient qu'un bit défini. Nous pouvons utiliser cette propriété et la fonction générique countSetBits pour déterminer si un nombre correspond à une puissance de 2 ou non.

Ceci est un programme C++:

int countSetBits(int n)
{
        int c = 0;
        while(n)
        {
                c += 1;
                n  = n & (n-1);
        }
        return c;
}

bool isPowerOfTwo(int n)
{        
        return (countSetBits(n)==1);
}
int main()
{
    int i, val[] = {0,1,2,3,4,5,15,16,22,32,38,64,70};
    for(i=0; i<sizeof(val)/sizeof(val[0]); i++)
        printf("Num:%d\tSet Bits:%d\t is power of two: %d\n",val[i], countSetBits(val[i]), isPowerOfTwo(val[i]));
    return 0;
}

Nous n'avons pas besoin de vérifier explicitement que 0 soit une puissance de 2, car elle renvoie False pour 0 également.

SORTIE

Num:0   Set Bits:0   is power of two: 0
Num:1   Set Bits:1   is power of two: 1
Num:2   Set Bits:1   is power of two: 1
Num:3   Set Bits:2   is power of two: 0
Num:4   Set Bits:1   is power of two: 1
Num:5   Set Bits:2   is power of two: 0
Num:15  Set Bits:4   is power of two: 0
Num:16  Set Bits:1   is power of two: 1
Num:22  Set Bits:3   is power of two: 0
Num:32  Set Bits:1   is power of two: 1
Num:38  Set Bits:3   is power of two: 0
Num:64  Set Bits:1   is power of two: 1
Num:70  Set Bits:3   is power of two: 0
3
jerrymouse

pour toute puissance de 2, ce qui suit est également valable.

n & (- n) == n

REMARQUE: échoue pour n = 0, vous devez donc le vérifier.
La raison pour laquelle cela fonctionne est:
- n est le complément à 2 de n. -n aura tous les bits situés à gauche du bit défini le plus à droite de n retourné par rapport à n. Pour une puissance de 2, il n'y a qu'un seul bit défini.

2
FReeze FRancis

Amélioration de la réponse de @ user134548, sans arithmétique des bits:

public static bool IsPowerOfTwo(ulong n)
{
    if (n % 2 != 0) return false;  // is odd (can't be power of 2)

    double exp = Math.Log(n, 2);
    if (exp != Math.Floor(exp)) return false;  // if exp is not integer, n can't be power
    return Math.Pow(2, exp) == n;
}

Cela fonctionne bien pour:

IsPowerOfTwo(9223372036854775809)
2
rhodan

Exemple

0000 0001    Yes
0001 0001    No

algorithme

  1. En utilisant un masque de bits, divisez NUM la variable en binaire

  2. IF R > 0 AND L > 0: Return FALSE

  3. Sinon, NUM devient celui qui est non nul

  4. IF NUM = 1: Return TRUE

  5. Sinon, passez à l'étape 1

Complexité

Time ~ O(log(d))d est le nombre de chiffres binaires

2
Khaled.K

Ce programme dans Java renvoie "true" si number est une puissance de 2 et renvoie "false" s'il ne s'agit pas d'une puissance de 2

// To check if the given number is power of 2

import Java.util.Scanner;

public class PowerOfTwo {
    int n;
    void solve() {
        while(true) {
//          To eleminate the odd numbers
            if((n%2)!= 0){
                System.out.println("false");
                break;
            }
//  Tracing the number back till 2
            n = n/2;
//  2/2 gives one so condition should be 1
            if(n == 1) {
                System.out.println("true");
                break;
            }
        }
    }
    public static void main(String[] args) {
        // TODO Auto-generated method stub
        Scanner in = new Scanner(System.in);
        PowerOfTwo obj = new PowerOfTwo();
        obj.n = in.nextInt();
        obj.solve();
    }

}

OUTPUT : 
34
false

16
true
1
Kaushik Holla
private static bool IsPowerOfTwo(ulong x)
{
    var l = Math.Log(x, 2);
    return (l == Math.Floor(l));
}
0
user134548

retourne i> 0 && (i ^ -i (-i << 1);) ==

Je n'ai pas trouvé une telle réponse. Que ce soit le mien

0
Aliaksei Yatsau