Làm thế nào để kiểm tra nếu một số là lũy thừa của 2


585

Hôm nay tôi cần một thuật toán đơn giản để kiểm tra xem một số có phải là lũy thừa của 2 không.

Thuật toán cần phải là:

  1. Đơn giản
  2. Đúng cho bất kỳ ulonggiá trị.

Tôi đã đưa ra thuật toán đơn giản này:

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

Nhưng sau đó tôi nghĩ, làm thế nào về việc kiểm tra nếu một số chính xác? Nhưng khi tôi kiểm tra 2 ^ 63 + 1, trả về chính xác 63 vì làm tròn số. Vì vậy, tôi đã kiểm tra xem 2 với công suất 63 có bằng số ban đầu không - và đó là vì tính toán được thực hiện bằng s và không phải là số chính xác:log2 xMath.Logdouble

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

Điều này trả về truecho giá trị sai đã cho : 9223372036854775809.

Có một thuật toán tốt hơn?


1
Tôi nghĩ rằng giải pháp (x & (x - 1))có thể trả về dương tính giả khi Xlà tổng lũy ​​thừa của hai, ví dụ 8 + 16.
Joe Brown

32
Tất cả các số có thể được viết dưới dạng tổng lũy ​​thừa của hai số, đó là lý do tại sao chúng ta có thể biểu diễn bất kỳ số nào trong hệ nhị phân. Hơn nữa, ví dụ của bạn không trả về kết quả dương tính giả, vì 11000 & 10111 = 10000! = 0.
vlsd

1
@JoeBrown Nó không có bất kỳ dương tính giả nào. Trong thực tế, biểu thức trả về giá trị lớn hơn của bất kỳ tổng hai lũy thừa nào.
Samy Bencherif

Câu trả lời:


1220

Có một mẹo đơn giản cho vấn đề này:

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

Lưu ý, chức năng này sẽ báo cáo truecho 0, mà không phải là một sức mạnh của 2. Nếu bạn muốn loại trừ điều đó, đây là cách:

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

Giải trình

Đầu tiên và trước hết là toán tử nhị phân & toán tử bit theo định nghĩa MSDN:

Nhị phân & toán tử được xác định trước cho các kiểu tích phân và bool. Đối với các kiểu tích phân, & tính toán logic bitwise AND của toán hạng của nó. Đối với các toán hạng bool, & tính toán logic của các toán hạng của nó; đó là, kết quả là đúng khi và chỉ khi cả hai toán hạng của nó là đúng.

Bây giờ chúng ta hãy xem làm thế nào tất cả diễn ra:

Hàm trả về boolean (true / false) và chấp nhận một tham số đến của kiểu không dấu dài (x, trong trường hợp này). Chúng ta hãy đơn giản giả sử rằng ai đó đã vượt qua giá trị 4 và gọi hàm như vậy:

bool b = IsPowerOfTwo(4)

Bây giờ chúng tôi thay thế mỗi lần xuất hiện của x bằng 4:

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

Vâng, chúng tôi đã biết rằng 4! = 0 evals thành đúng, cho đến nay là rất tốt. Nhưng những gì về:

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

Điều này có nghĩa là tất nhiên:

((4 & 3) == 0)

Nhưng chính xác thì 4&3sao?

Biểu diễn nhị phân của 4 là 100 và biểu diễn nhị phân của 3 là 011 (hãy nhớ & lấy biểu diễn nhị phân của các số này). Vì vậy chúng tôi có:

100 = 4
011 = 3

Hãy tưởng tượng những giá trị này được xếp chồng lên nhau giống như phép cộng cơ bản. Các &nhà điều hành nói rằng nếu cả hai giá trị bằng 1 thì kết quả là 1, ngược lại là 0. Vì vậy 1 & 1 = 1, 1 & 0 = 0, 0 & 0 = 0, và 0 & 1 = 0. Vì vậy, chúng tôi làm toán:

100
011
----
000

Kết quả chỉ đơn giản là 0. Vì vậy, chúng tôi quay lại và xem những gì tuyên bố hoàn trả của chúng tôi bây giờ chuyển thành:

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

Bây giờ dịch sang:

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

Chúng ta đều biết rằng điều đó true && truechỉ đơn giản truevà điều này cho thấy ví dụ của chúng ta, 4 là sức mạnh của 2.


56
@Kripp: Số sẽ có dạng nhị phân 1000 ... 000. Khi bạn -1 nó, nó sẽ có dạng 0111 ... 111. Do đó, nhị phân của hai số và sẽ có kết quả là 000000. Điều này sẽ không xảy ra đối với những người không có quyền lực, vì 1010100 chẳng hạn sẽ trở thành 1010011, dẫn đến một
cấu hình

47
... Kết quả là 1010000 sau nhị phân và. Dương tính giả duy nhất sẽ là 0, đó là lý do tại sao tôi sẽ sử dụng: return (x! = 0) && ((x & (x - 1)) == 0);
cấu hình

6
Kripp, xem xét (2: 1, 10: 1) (4: 3, 100: 11) (8: 7, 1000: 111) (16:15, 10000: 1111) Xem mẫu?
Thomas L Holaday

13
@ShuggyCoUk: phần bù của hai là cách biểu thị số âm. Vì đây là số nguyên không dấu, nên việc biểu diễn các số âm không liên quan. Kỹ thuật này chỉ dựa vào biểu diễn nhị phân của các số nguyên không âm.
Greg Hewgill

4
@SoapBox - cái gì phổ biến hơn? Số không hoặc số không khác không có lũy thừa của hai? Đây là một câu hỏi bạn không thể trả lời mà không có thêm một số bối cảnh. Và nó thực sự, thực sự không có vấn đề gì.
cấu hình

97

Một số trang web tài liệu và giải thích điều này và các bản hack đôi chút khác là:

Và ông lớn của họ, cuốn sách "Hackman Delight" của Henry Warren, Jr . :

Như trang của Sean Anderson giải thích, biểu thức ((x & (x - 1)) == 0)không chính xác chỉ ra rằng 0 là lũy thừa của 2. Ông gợi ý nên sử dụng:

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

để khắc phục vấn đề đó.


4
0 là lũy thừa của 2 ... 2 ^ -inf = 0 .;););)
Michael Bray

4
Vì đây là một luồng được gắn thẻ C # , nên đáng chú ý rằng biểu thức cuối cùng (của Sean Anderson) là bất hợp pháp trong C # vì !chỉ có thể được áp dụng cho các kiểu boolean và &&cũng yêu cầu cả hai toán hạng phải là boolean- (Ngoại trừ toán tử do người dùng xác định làm cho những thứ khác có thể, nhưng điều đó không liên quan ulong.)
Jeppe Stig Nielsen

40

return (i & -i) == i


2
bất kỳ gợi ý tại sao điều này sẽ hoặc sẽ không làm việc? tôi chỉ kiểm tra tính chính xác của nó trong java, trong đó chỉ có ints / longs đã ký. Nếu nó đúng, đây sẽ là câu trả lời vượt trội. nhanh hơn + nhỏ hơn
Andreas Petersson

7
Nó tận dụng một trong các thuộc tính của ký hiệu hai phần bù: để tính giá trị âm của một số bạn thực hiện phủ định theo bit và thêm 1 vào kết quả. Bit quan trọng nhất iđược thiết lập cũng sẽ được thiết lập -i. Các bit bên dưới sẽ là 0 (trong cả hai giá trị) trong khi các bit ở trên nó sẽ được đảo ngược với nhau. Do đó, giá trị của i & -iý chí sẽ là bit thiết lập ít có ý nghĩa nhất i(là lũy thừa của hai). Nếu icó cùng giá trị thì đó là tập bit duy nhất. Nó thất bại khi ilà 0 vì lý do tương tự i & (i - 1) == 0.
Michael Carman

6
Nếu ilà một loại không dấu, bổ sung twos không có gì để làm với nó. Bạn chỉ đang tận dụng các tính chất của số học mô-đun và bitwise và.
R .. GitHub DỪNG GIÚP ICE

2
Điều này không hoạt động nếu i==0(trả về (0&0==0)đó là true). Nó nên làreturn i && ( (i&-i)==i )
bobobobo

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

3
Giải pháp này tốt hơn bởi vì nó cũng có thể xử lý số âm nếu âm có thể truyền vào. (Nếu dài thay vì ulong)
Steven

Tại sao một số thập phân vượt qua như một sức mạnh của hai trong trường hợp này?
chris Frisina


17

Đây là một giải pháp C ++ đơn giản :

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

8
trên gcc này sẽ biên dịch thành một gcc dựng sẵn được gọi __builtin_popcount. Thật không may, một họ bộ xử lý chưa có một hướng dẫn lắp ráp duy nhất để làm điều này (x86), vì vậy thay vào đó, đây là phương pháp nhanh nhất để đếm bit. Trên bất kỳ kiến ​​trúc nào khác, đây là một hướng dẫn lắp ráp duy nhất.
deft_code

3
@deft_code hỗ trợ vi kiến ​​trúc x86 mới hơnpopcnt
phuclv

13

Phụ lục sau đây cho câu trả lời được chấp nhận có thể hữu ích cho một số người:

Sức mạnh của hai, khi được biểu thị dưới dạng nhị phân, sẽ luôn trông giống như 1 theo sau là n số 0 trong đó n lớn hơn hoặc bằng 0. Ví dụ:

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

và như thế.

Khi chúng ta trừ đi 1các loại số này, chúng trở thành 0 theo sau là n và một lần nữa n giống như trên. Ví dụ:

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

và như thế.

Đến mấu chốt

Điều gì xảy ra khi chúng ta thực hiện một bit VÀ một số x, đó là lũy thừa của 2 và x - 1?

Một trong số xđược căn chỉnh bằng 0 x - 1và tất cả các số 0 xđược căn chỉnh với số 0 x - 1, khiến bitwise AND dẫn đến 0. Và đó là cách chúng ta có câu trả lời duy nhất được đề cập ở trên là đúng.


Thêm vào vẻ đẹp của câu trả lời được chấp nhận ở trên -

Vì vậy, chúng tôi có một tài sản theo ý của chúng tôi bây giờ:

Khi chúng ta trừ 1 từ bất kỳ số nào, thì trong biểu diễn nhị phân, số 1 bên phải sẽ trở thành 0 và tất cả các số 0 trước số 1 bên phải đó sẽ trở thành 1

Một ứng dụng tuyệt vời của tài sản này là trong việc tìm hiểu - Có bao nhiêu số 1 trong biểu diễn nhị phân của một số đã cho? Mã ngắn và ngọt để làm điều đó cho một số nguyên cho trước xlà:

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

Một khía cạnh khác của các số có thể được chứng minh từ khái niệm được giải thích ở trên là "Mọi số dương có thể được biểu diễn dưới dạng tổng lũy ​​thừa của 2 không?".

Có, mọi số dương có thể được biểu diễn dưới dạng tổng lũy ​​thừa của 2. Đối với bất kỳ số nào, hãy lấy biểu diễn nhị phân của nó. Vd: Lấy số 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

@Michi: Tôi có khẳng định ở đâu đó rằng 0 là số dương không? Hay một sức mạnh của 2?
displayName

Có, bằng cách đặt 0 làm ví dụ và thực hiện phép toán đó trên nó trong biểu diễn nhị phân đó. Nó tạo ra một sự nhầm lẫn.
Michi

1
Nếu thêm hai số khiến bạn nhầm lẫn rằng họ phải tích cực, tôi không thể làm gì được. Hơn nữa, 0 đã được thể hiện trong đại diện để ngụ ý rằng sức mạnh của 2 được bỏ qua trong số này. Bất cứ ai biết toán học cơ bản đều biết rằng thêm 0 có nghĩa là không thêm bất cứ thứ gì.
displayName

10

Sau khi đăng câu hỏi tôi nghĩ đến giải pháp sau:

Chúng ta cần kiểm tra xem chính xác một trong các chữ số nhị phân là một. Vì vậy, chúng ta chỉ cần thay đổi số đúng một chữ số tại một thời điểm và trả về truenếu nó bằng 1. Nếu tại bất kỳ thời điểm nào chúng ta đến bởi một số lẻ ( (number & 1) == 1), chúng ta biết kết quả là false. Điều này đã chứng minh (sử dụng điểm chuẩn) nhanh hơn một chút so với phương pháp ban đầu đối với các giá trị đúng (lớn) và nhanh hơn nhiều đối với các giá trị sai hoặc nhỏ.

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

Tất nhiên, giải pháp của Greg tốt hơn nhiều.


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

Và đây là một thuật toán chung để tìm hiểu xem một số có phải là lũy thừa của một số khác không.

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

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

1
Đây có phải là c#? Tôi đoán điều này c++như xđược trả lại như là một bool.
Mariano Desanze

1
Tôi đã viết nó là C ++. Để làm cho nó C # là tầm thường: bool isPow2 = ((x & ~ (x-1)) == x)? x! = 0: sai;
abelenky

4

Tìm nếu số đã cho là lũy thừa của 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;
}

Hoặc, trong C #: return x == Math.Pow (2, Math.Log (x, 2));
cấu hình

4
Bị hỏng. Đau khổ từ các vấn đề làm tròn điểm nổi lớn. Sử dụng frexpthay vì những logthứ khó chịu nếu bạn muốn sử dụng dấu chấm động.
R .. GitHub DỪNG GIÚP ICE

4
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
int isPowerOfTwo(unsigned int x)
{
    return ((x != 0) && ((x & (~x + 1)) == x));
}

Điều này thực sự nhanh chóng. Mất khoảng 6 phút 43 giây để kiểm tra tất cả 2 ^ 32 số nguyên.


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

Nếu xlà một sức mạnh của hai, 1 bit đơn độc của nó ở vị trí n. Điều này có nghĩa là x – 1có 0 ở vị trí n. Để xem tại sao, hãy nhớ lại cách hoạt động của phép trừ nhị phân. Khi trừ 1 từ x, khoản vay lan truyền tất cả các vị trí n; bit ntrở thành 0 và tất cả các bit thấp hơn trở thành 1. Bây giờ, vì xkhông có 1 bit nào giống với x – 1, x & (x – 1)là 0 và !(x & (x – 1))là đúng.


3

Một số là lũy thừa 2 nếu chỉ chứa 1 bit set. Chúng ta có thể sử dụng thuộc tính này và hàm chungcountSetBits để tìm xem một số có lũy thừa 2 hay không.

Đây là chương trình 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;
}

Chúng ta không cần phải kiểm tra rõ ràng 0 là Sức mạnh của 2, vì nó cũng trả về Sai cho 0.

ĐẦU RA

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

Trả về c dưới dạng 'int' khi hàm có kiểu trả về 'ulong'? Sử dụng whilethay vì một if? Cá nhân tôi không thể thấy một lý do nhưng nó có vẻ hiệu quả. EDIT: - không ... nó sẽ trả về 1 cho bất cứ điều gì lớn hơn 0!?
James Khoury

@JamesKhoury Tôi đang viết một chương trình c ++ nên tôi đã trả lại nhầm một int. Tuy nhiên, đó là một lỗi chính tả nhỏ và không đáng bị downvote. Nhưng tôi không hiểu lý do cho phần còn lại của bình luận của bạn "sử dụng while thay vì if" và "nó sẽ trả về 1 cho bất cứ điều gì lớn hơn 0". Tôi đã thêm sơ khai chính để kiểm tra đầu ra. AFAIK là sản lượng dự kiến ​​của nó. Đúng nếu tôi đã sai lầm.
jerrymouse

3

Đây là một phương pháp khác mà tôi đã nghĩ ra, trong trường hợp này là sử dụng |thay vì &:

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

Bạn có cần (x > 0)bit ở đây không?
cấu hình

@configurator, vâng, nếu không thì is_power_of_2 (0) sẽ trở lại đúng
Chethan

3

cho bất kỳ quyền lực của 2, sau đây cũng giữ.

n & (- n) == n

LƯU Ý: thất bại cho n = 0, vì vậy cần kiểm tra xem
Lý do tại sao điều này hoạt động là:
-n là phần bù 2s của n. -n sẽ có mọi bit ở bên trái của bit được đặt ngoài cùng bên phải của n được lật so với n. Đối với quyền hạn 2 chỉ có một bit set.


2

Thí dụ

0000 0001    Yes
0001 0001    No

Thuật toán

  1. Sử dụng mặt nạ bit, chia NUMbiến trong nhị phân

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

  3. Mặt khác, NUMtrở thành một cái khác không

  4. IF NUM = 1: Return TRUE

  5. Nếu không, hãy đến Bước 1

Phức tạp

Thời gian ~ O(log(d))nơi dlà số chữ số nhị phân


1

Cải thiện câu trả lời của @ user134548, không có số học bit:

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

Điều này hoạt động tốt cho:

IsPowerOfTwo(9223372036854775809)

Các phép toán dấu phẩy động chậm hơn nhiều so với một biểu thức bitwise đơn giản
phuclv

1

Đánh dấu gravell đã đề xuất điều này nếu bạn có .NET Core 3, System.R nb.Intrinsics.X86.Popcnt.PopCount

public bool IsPowerOfTwo(uint i)
{
    return Popcnt.PopCount(i) == 1
}

Hướng dẫn đơn, nhanh hơn (x != 0) && ((x & (x - 1)) == 0)nhưng ít di động.


bạn có chắc là nó nhanh hơn (x != 0) && ((x & (x - 1)) == 0)không? Tôi nghi ngờ điều đó, đặc biệt. trên các hệ thống cũ hơn, nơi popcnt không có sẵn
phuclv

Nó không nhanh hơn. Tôi vừa thử nghiệm điều này trên CPU Intel hiện đại và POPCNT đã được xác minh sử dụng trong quá trình tháo gỡ (được cấp, bằng mã C, không phải .NET). POPCNT nhanh hơn để đếm bit nói chung, nhưng đối với trường hợp một bit, thủ thuật vặn bit vẫn nhanh hơn 10%.
kỷ nguyên

Rất tiếc, tôi lấy lại. Tôi đã thử nghiệm trong một vòng lặp là tôi nghĩ dự đoán chi nhánh là "gian lận". POPCNT thực sự là một lệnh duy nhất chạy trong một chu kỳ đồng hồ duy nhất và nhanh hơn nếu bạn có sẵn.
kỷ nguyên

0

Trong C, tôi đã thử nghiệm i && !(i & (i - 1)mẹo và so sánh nó với__builtin_popcount(i) , sử dụng gcc trên Linux, với cờ -mpopcnt để chắc chắn sử dụng lệnh POPCNT của CPU. Chương trình thử nghiệm của tôi đã đếm # số nguyên trong khoảng từ 0 đến 2 ^ 31 là lũy thừa của hai.

Lúc đầu, tôi nghĩ rằng nó i && !(i & (i - 1)nhanh hơn 10%, mặc dù tôi đã xác minh rằng POPCNT đã được sử dụng trong quá trình tháo gỡ mà tôi đã sử dụng__builtin_popcount .

Tuy nhiên, tôi nhận ra rằng tôi đã bao gồm một câu lệnh if và dự đoán nhánh có thể hoạt động tốt hơn trên phiên bản xoay vòng bit. Tôi đã loại bỏ if và POPCNT kết thúc nhanh hơn, như mong đợi.

Các kết quả:

Intel (R) Core (TM) i7-4771 CPU tối đa 3,90GHz

Timing (i & !(i & (i - 1))) trick
30

real    0m13.804s
user    0m13.799s
sys     0m0.000s

Timing POPCNT
30

real    0m11.916s
user    0m11.916s
sys     0m0.000s

Bộ xử lý AMD Ryzen Threadripper 2950X 16 nhân tối đa 3.50GHz

Timing (i && !(i & (i - 1))) trick
30

real    0m13.675s
user    0m13.673s
sys 0m0.000s

Timing POPCNT
30

real    0m13.156s
user    0m13.153s
sys 0m0.000s

Lưu ý rằng ở đây CPU Intel có vẻ chậm hơn một chút so với AMD với một chút thay đổi, nhưng có POPCNT nhanh hơn nhiều; AMD POPCNT không cung cấp nhiều sự tăng cường.

popcnt_test.c:

#include "stdio.h"

// Count # of integers that are powers of 2 up to 2^31;
int main() {
  int n;
  for (int z = 0; z < 20; z++){
      n = 0;
      for (unsigned long i = 0; i < 1<<30; i++) {
       #ifdef USE_POPCNT
        n += (__builtin_popcount(i)==1); // Was: if (__builtin_popcount(i) == 1) n++;
       #else
        n += (i && !(i & (i - 1)));  // Was: if (i && !(i & (i - 1))) n++;
       #endif
      }
  }

  printf("%d\n", n);
  return 0;
}

Chạy thử nghiệm:

gcc popcnt_test.c -O3 -o test.exe
gcc popcnt_test.c -O3 -DUSE_POPCNT -mpopcnt -o test-popcnt.exe

echo "Timing (i && !(i & (i - 1))) trick"
time ./test.exe

echo
echo "Timing POPCNT"
time ./test-opt.exe

0

Tôi thấy nhiều câu trả lời đang đề xuất trả về n &&! (N & (n - 1)) nhưng theo kinh nghiệm của tôi nếu các giá trị đầu vào là âm thì nó trả về giá trị sai. Tôi sẽ chia sẻ một cách tiếp cận đơn giản khác ở đây vì chúng ta biết một lũy thừa gồm hai số chỉ có một bit được đặt nên đơn giản là chúng ta sẽ đếm số bit được đặt sẽ mất thời gian O (log N).

while (n > 0) {
    int count = 0;
    n = n & (n - 1);
    count++;
}
return count == 1;

Kiểm tra bài viết này để đếm không. của bit thiết lập


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

Hãy thử điều đó cho số 9223372036854775809. Nó có hoạt động không? Tôi nghĩ là không, vì lỗi làm tròn số.
cấu hình

1
@configurator 922337203685477580_9_ không giống như sức mạnh của 2 đối với tôi;)
Kirschstein

1
@Kirschstein: con số đó đã cho anh ta một dương tính giả.
Erich Mirabal

7
Kirschstein: Nó cũng không giống với tôi. Nó trông giống như một chức năng mặc dù ...
cấu hình

-2

Chương trình này trong java trả về "true" nếu số là lũy thừa 2 và trả về "false" nếu nó không phải là lũy thừa 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
câu hỏi này được gắn thẻ C # và giải pháp của bạn cũng rất chậm so với các giải pháp trước đây [
phuclv
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.