Как проверить, является ли число мощностью 2

Сегодня мне нужен простой алгоритм для проверки того, является ли число мощностью 2.

Алгоритм должен быть:

  • Простой
  • Корректно для любого значения ulong.

Я придумал этот простой алгоритм:

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

Но потом я подумал, как насчет проверки того, является ли log2 x точно круглым числом? Но когда я проверил 2 ^ 63 + 1, Math.Log вернул ровно 63 из-за округления. Итак, я проверил, соответствует ли 2 мощности 63 исходному числу, и это потому, что вычисление выполняется в double, а не в точном числе:

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

Это возвращало true для данного неправильного значения: 9223372036854775809.

Есть ли лучший алгоритм?

Ответ 1

Там простой трюк для этой проблемы:

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

Обратите внимание: эта функция будет сообщать значение true для 0, что не является значением 2. Если вы хотите исключить это, вот как:

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

объяснение

Прежде всего, побитовый двоичный оператор из определения MSDN:

Бинарные и операторы предопределены для интегральных типов и bool. Для интегральных типов & вычисляет логическое побитовое И его операндов. Для операндов bool и вычисляет логический AND своих операндов; то есть результат верен тогда и только тогда, когда оба его операнда верны.

Теперь давайте посмотрим, как все это происходит:

Функция возвращает boolean (true/false) и принимает один входящий параметр типа unsigned long (x, в этом случае). Для простоты предположим, что кто-то прошел значение 4 и назвал функцию так:

bool b = IsPowerOfTwo(4)

Теперь мы заменяем каждое вхождение x на 4:

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

Ну, мы уже знаем, что 4! = 0 оценивает истину, пока что так хорошо. Но что насчет:

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

Это, конечно же, означает:

((4 & 3) == 0)

Но что такое 4&3?

Бинарное представление 4 равно 100, а двоичное представление 3 равно 011 (помните, что & берет двоичное представление этих чисел). Таким образом, мы имеем:

100 = 4
011 = 3

Представьте, что эти значения сложены как элементарное дополнение. Оператор & говорит, что если оба значения равны 1, то результат равен 1, иначе оно равно 0. Итак, 1 & 1 = 1, 1 & 0 = 0, 0 & 0 = 0 и 0 & 1 = 0. Итак, мы делаем математику:

100
011
----
000

Результат - просто 0. Итак, мы вернемся и посмотрим, что теперь означает наш оператор return:

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

Это переводит теперь:

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

Мы все знаем, что true && true просто true, и это показывает, что для нашего примера 4 - это сила 2.

Ответ 2

Некоторые сайты, которые документируют и объясняют этот и другие хитрые взломы:

И их дедушка, книга "Восхищение Хакера" Генри Уоррена-младшего:

Как объясняет страница Шона Андерсона, выражение ((x & (x - 1)) == 0) неверно указывает, что 0 - это степень 2. Он предлагает использовать:

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

чтобы исправить эту проблему.

Ответ 3

return (i & -i) == i

Ответ 4

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

Ответ 6

Здесь простое решение C++:

bool IsPowerOfTwo( unsigned int i )
{
    return std::bitset<32>(i).count() == 1;
}

Ответ 7

После публикации вопроса я подумал о следующем решении:

Нам нужно проверить, является ли одна из двоичных цифр одной. Поэтому мы просто сдвигаем число справа на одну цифру за раз и возвращаем true, если оно равно 1. Если в какой-либо точке мы получаем нечетное число ((number & 1) == 1), мы знаем, что результат false. Это подтвердилось (с использованием эталона) несколько быстрее, чем исходный метод для (больших) значений истины и намного быстрее для ложных или малых значений.

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 not a power of two.
            return false;

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

Конечно, решение Грега намного лучше.

Ответ 8

    bool IsPowerOfTwo(int n)
    {
        if (n > 1)
        {
            while (n%2 == 0)
            {
                n >>= 1;
            }
        }
        return n == 1;
    }

И вот общий алгоритм для выяснения, является ли число степенью другого числа.

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

Ответ 9

Следующее дополнение к принятому ответу может быть полезно для некоторых людей:

Степень двойки, выраженная в двоичном виде, всегда будет выглядеть как 1, за которым следуют n нулей, где n больше или равно 0. Пример:

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

и так далее.

Когда мы вычитаем 1 из таких чисел, они становятся 0, за которыми следуют n и снова n такое же, как указано выше. Пример:

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

и так далее.

Подойдя к сути

Что происходит, когда мы делаем побитовое И для числа x, которое является степенью 2, а x - 1?

Один из x выравнивается с нулем x - 1 и все нули x выравниваются с нулями x - 1, в результате чего побитовое И приводит к 0. И вот как мы получили ответ из одной строки, упомянутый выше: право.


Дальнейшее добавление к красоте принятого ответа выше -

Итак, теперь у нас есть недвижимость:

Когда мы вычтем 1 из любого числа, то в двоичном представлении крайний правый 1 станет 0, а все нули до этого самого правого 1 станут 1

Одним из удивительных применений этого свойства является выяснение - сколько единиц присутствует в двоичном представлении данного числа? Короткий и приятный код для этого целого числа x:

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

Другой аспект чисел, который может быть доказан из концепции, объясненной выше: "Может ли каждое положительное число быть представлено как сумма степеней 2?" ,

Да, каждое положительное число может быть представлено как сумма степеней 2. Для любого числа возьмите его двоичное представление. Пример: взять номер 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

bool isPow2 = ((x & ~(x-1))==x)? !!x : 0;

Ответ 11

Найти, если заданное число является степенью 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;
}

Ответ 12

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

Ответ 13

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

Это очень быстро. Для проверки всех 2 ^ 32 целых чисел требуется около 6 минут и 43 секунды.

Ответ 14

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

Если x - это мощность двух, то его 1-й бит находится в положении n. Это означает, что x – 1 имеет 0 в позиции n. Чтобы понять, почему, вспомните, как работает двоичное вычитание. При вычитании 1 из x заимствование распространяется до положения n; бит n становится 0, а все нижние биты становятся равными 1. Теперь, поскольку x не имеет 1 бита, общего с x – 1, x & (x – 1) равно 0, а !(x & (x – 1)) - true.

Ответ 15

Число - это значение 2, если оно содержит только 1 бит набора. Мы можем использовать это свойство и общую функцию countSetBits, чтобы определить, имеет ли число значение 2 или нет.

Это программа на С++:

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

Нам не нужно явно проверять, что 0 является степенью 2, так как он также возвращает False для 0.

OUTPUT

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

Ответ 16

Вот еще один метод, который я разработал в этом случае, используя | вместо &:

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

Ответ 17

Пример

0000 0001    Yes
0001 0001    No

Алгоритм

  • Используя битовую маску, разделите NUM переменную в двоичном

  • IF R > 0 AND L > 0: Return FALSE

  • В противном случае NUM становится ненулевым

  • IF NUM = 1: Return TRUE

  • В противном случае перейдите к Шагу 1

Сложность

Время ~ O(log(d)), где d - число двоичных цифр

Ответ 18

для любой степени 2, справедливо также следующее.

п & (- п) == п

ПРИМЕЧАНИЕ: сбой при n = 0, поэтому необходимо проверить его Причина, почему это работает:
-n - 2s-дополнение к n. -n будет иметь каждый бит слева от самого правого набора бит n flipped по сравнению с n. Для степеней 2 существует только один бит набора.

Ответ 19

Улучшение ответа @user134548 без арифметики бит:

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

Это отлично подходит для:

IsPowerOfTwo(9223372036854775809)

Ответ 20

Эта программа в java возвращает "true", если число является степенью 2 и возвращает "false", если ее значение не равно 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

Ответ 21

private static bool IsPowerOfTwo(ulong x)
{
    var l = Math.Log(x, 2);
    return (l == Math.Floor(l));
}

Ответ 22

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

Не нашел такого ответа. Пусть это будет мое

Ответ 23

public static IsPowerOf(int num, int powerOf)
{
    var logOfNum = Math.Round(Math.Log(num, powerOf), 10);
    // Check for Integral.
    return (logOfNum % 1) == 0;
}