Cách đơn giản nhất để kiểm tra xem một số có phải là lũy thừa của 2 trong C ++ hay không?


94

Tôi cần một chức năng như thế này:

// return true iff 'n' is a power of 2, e.g.
// is_power_of_2(16) => true  is_power_of_2(3) => false
bool is_power_of_2(int n);

Bất cứ ai có thể đề nghị làm thế nào tôi có thể viết điều này? Bạn có thể cho tôi biết một trang web tốt có thể tìm thấy loại thuật toán này không?



@rootTraveller - Có thể không phải là bản sao. C ++ và Java là các ngôn ngữ khác nhau và mỗi ngôn ngữ cung cấp các tiện ích khác nhau. Ví dụ: Trong C / C ++, bây giờ chúng ta có thể sử dụng bản chất với bộ xử lý hỗ trợ BMI, điều này đưa ra lệnh máy để thực hiện nó trong một lần đồng hồ. Tôi tưởng tượng Java có những thứ khác, như có thể là một thứ gì đó từ một thói quen Toán học.
jww 20/09/2016

Câu trả lời:


190

(n & (n - 1)) == 0là tốt nhất. Tuy nhiên, lưu ý rằng nó sẽ trả về true cho n = 0 một cách không chính xác, vì vậy nếu điều đó có thể xảy ra, bạn sẽ muốn kiểm tra nó một cách rõ ràng.

http://www.graphics.stanford.edu/~seander/bithacks.html có một bộ sưu tập lớn các thuật toán xoay bit thông minh, bao gồm cả thuật toán này.


8
nên về cơ bản(n>0 && ((n & (n-1)) == 0))
Saurabh Goyal

1
@SaurabhGoyal hoặc n && !(n & (n - 1))dưới dạng liên kết trong các trạng thái câu trả lời.
Carsten

Tại sao, oh tại sao, đây không phải là đầu câu trả lời? OP vui lòng chấp nhận.
donturner

@SaurabhGoyal Một cải tiến nhỏ là thế này: n & !(n & (n - 1)). Chú ý bitwise AND &(không logic và &&). Các toán tử bitwise không thực hiện đoản mạch và do đó, mã không phân nhánh. Điều này thích hợp hơn trong các tình huống có khả năng xảy ra sai sót nhánh và khi tính toán rhs của biểu thức (nghĩa là !(n & (n - 1))) là rẻ.
Cassio Neri

@cassio !là một toán tử logic và do đó giá trị của !(n & (n - 1))sẽ là một boolean, Bạn có chắc chắn một boolean và một số có thể được cấp cho toán tử AND bitwise không? Nếu có, nó có vẻ tốt.
Saurabh Goyal

81

Một lũy thừa của hai sẽ chỉ có một bit được đặt (đối với các số không có dấu). Cái gì đó như

bool powerOfTwo = !(x == 0) && !(x & (x - 1));

Sẽ hoạt động tốt; một ít hơn lũy thừa của hai đều là 1s trong các bit ít quan trọng hơn, vì vậy phải AND đến 0 theo chiều dọc bit.

Vì tôi đã giả sử các số không có dấu, nên kiểm tra == 0 (mà tôi ban đầu đã quên, xin lỗi) là đủ. Bạn có thể muốn kiểm tra> 0 nếu bạn đang sử dụng số nguyên có dấu.


Bạn đang thiếu một '!' hoặc '== 0'

Bạn cũng đang thiếu một bài kiểm tra cho giá trị âm của x.
Rob Wells 20-08

Gọn gàng, làm thế nào bạn chỉnh sửa nó mà không xuất hiện 'đã chỉnh sửa x phút trước'?

Nghiêm túc mà nói, làm thế nào bạn vừa nhận được 120 đại diện cho một câu trả lời sai rõ ràng?

@Mike F: Thật vậy, có vẻ như mọi người sẽ bỏ phiếu cho các câu trả lời mà không cần kiểm tra chúng. Tôi đoán là ai cũng có thể mắc lỗi - nếu tôi mắc lỗi nào trong tương lai, hãy thoải mái chỉnh sửa chúng.
Adam Wright

49

Quyền hạn của hai trong hệ nhị phân trông như thế này:

1: 0001
2: 0010
4: 0100
8: 1000

Lưu ý rằng luôn có chính xác 1 bit được đặt. Ngoại lệ duy nhất là với một số nguyên có dấu. ví dụ: Một số nguyên có dấu 8 bit với giá trị -128 trông giống như:

10000000

Vì vậy, sau khi kiểm tra rằng số lớn hơn 0, chúng ta có thể sử dụng một mẹo nhỏ thông minh để kiểm tra một số đó và chỉ một bit được đặt.

bool is_power_of_2(int x) {
    return x > 0 && !(x & (x1));
}

Để biết thêm thông tin chi tiết, hãy xem tại đây .


14

Cách tiếp cận # 1:

Chia số cho 2 ẩn để kiểm tra nó.

Thời gian phức tạp: O (log2n).

Cách tiếp cận # 2:

Bitwise VÀ số với số trước đó của nó phải bằng ZERO.

Thí dụ: Number = 8 Binary of 8: 1 0 0 0 Binary of 7: 0 1 1 1 và bitwise AND của cả hai số là 0 0 0 0 = 0.

Thời gian phức tạp: O (1).

Cách tiếp cận # 3:

Bitwise XOR số với số trước đó của nó phải là tổng của cả hai số.

Ví dụ: Number = 8 Binary of 8: 1 0 0 0 Binary of 7: 0 1 1 1 và bitwise XOR của cả hai số là 1 1 1 1 = 15.

Độ phức tạp về thời gian: O (1).

http://javaexplorer03.blogspot.in/2016/01/how-to-check-number-is-power-of-two.html


8
bool is_power_of_2(int i) {
    if ( i <= 0 ) {
        return 0;
    }
    return ! (i & (i-1));
}

7

đối với bất kỳ lũy thừa nào của 2, điều sau đây cũng được giữ nguyên.

n & (- n) == n

LƯU Ý: Điều kiện đúng với n = 0, mặc dù nó không phải là lũy thừa của 2.
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 đặt ngoài cùng bên phải của n bị lật so với n. Đối với lũy thừa của 2, chỉ có một bit đặt.


2
ý tôi là điều kiện đúng với n = 0 mặc dù nó không phải là
lũy thừa

điều này có thực hiện với các chuyển đổi xảy ra nếu n không có dấu?
Joseph Garvin

5

Trong C ++ 20 có std::ispow2 , bạn có thể sử dụng chính xác mục đích này nếu bạn không cần tự thực hiện:

#include <bit>
static_assert(std::ispow2(16));
static_assert(!std::ispow2(15));

5

Đây có lẽ là nhanh nhất, nếu sử dụng GCC. Nó chỉ sử dụng một lệnh POPCNT cpu và một phép so sánh. Biểu diễn nhị phân của bất kỳ lũy thừa nào của 2 số, luôn chỉ có một bit được thiết lập, các bit khác luôn bằng 0. Vì vậy, chúng tôi đếm số bit thiết lập với POPCNT, và nếu nó bằng 1, số đó là lũy thừa của 2. Tôi không nghĩ có bất kỳ phương pháp nào có thể nhanh hơn. Và nó rất đơn giản, nếu bạn đã hiểu nó một lần:

if(1==__builtin_popcount(n))

Không. Tôi vừa thử nghiệm cái này. Tôi thích popcount nhưng đối với bài kiểm tra sức mạnh của 2, bài kiểm tra i && !(i & (i - 1)))này nhanh hơn khoảng 10% trên máy của tôi, ngay cả khi tôi đã chắc chắn bật lệnh POPCNT lắp ráp gốc trong gcc.
eraoul

Rất tiếc, tôi lấy lại. Chương trình thử nghiệm của tôi đang chạy trong một vòng lặp và dự đoán nhánh là "gian lận". Bạn nói đúng, nếu bạn có lệnh POPCNT trên CPU của mình, nó sẽ nhanh hơn.
eraoul

3

Sau đó sẽ nhanh hơn câu trả lời được nhiều người bình chọn nhất do đoản mạch boolean và thực tế là so sánh chậm.

int isPowerOfTwo(unsigned int x)
{
  return x && !(x & (x  1));
}

Nếu bạn biết rằng x không thể là 0 thì

int isPowerOfTwo(unsigned int x)
{
  return !(x & (x  1));
}


3

Cách đơn giản nhất để kiểm tra xem một số có phải là lũy thừa của 2 trong C ++ hay không?

Nếu bạn có bộ xử lý Intel hiện đại với Hướng dẫn thao tác bit , thì bạn có thể thực hiện các thao tác sau. Nó bỏ qua mã C / C ++ thông thường vì những người khác đã trả lời nó, nhưng bạn cần nó nếu BMI không có sẵn hoặc không được kích hoạt.

bool IsPowerOf2_32(uint32_t x)
{
#if __BMI__ || ((_MSC_VER >= 1900) && defined(__AVX2__))
    return !!((x > 0) && _blsr_u32(x));
#endif
    // Fallback to C/C++ code
}

bool IsPowerOf2_64(uint64_t x)
{
#if __BMI__ || ((_MSC_VER >= 1900) && defined(__AVX2__))
    return !!((x > 0) && _blsr_u64(x));
#endif
    // Fallback to C/C++ code
}

Hỗ trợ BMI tín hiệu GCC, ICC và Clang với __BMI__. Nó có sẵn trong trình biên dịch của Microsoft trong Visual Studio 2015 trở lên khi AVX2 có sẵn và được kích hoạt . Đối với các tiêu đề bạn cần, hãy xem Tệp tiêu đề để biết bản chất của SIMD .

Tôi thường bảo vệ _blsr_u64bằng một _LP64_biên dịch trong trường hợp trên i686. Clang cần một chút cách giải quyết vì nó sử dụng một biểu tượng nội tại hơi khác nam:

#if defined(__GNUC__) && defined(__BMI__)
# if defined(__clang__)
#  ifndef _tzcnt_u32
#   define _tzcnt_u32(x) __tzcnt_u32(x)
#  endif
#  ifndef _blsr_u32
#    define  _blsr_u32(x)  __blsr_u32(x)
#  endif
#  ifdef __x86_64__
#   ifndef _tzcnt_u64
#    define _tzcnt_u64(x) __tzcnt_u64(x)
#   endif
#   ifndef _blsr_u64
#     define  _blsr_u64(x)  __blsr_u64(x)
#   endif
#  endif  // x86_64
# endif  // Clang
#endif  // GNUC and BMI

Bạn có thể cho tôi biết một trang web tốt có thể tìm thấy loại thuật toán này không?

Trang web này thường được trích dẫn: Bit Twiddling Hacks .


Đây chắc chắn không phải là "cách đơn giản nhất" theo yêu cầu trong OP, nhưng được cho là nhanh nhất đối với các môi trường cụ thể. Việc chỉ ra cách điều chỉnh cho các kiến ​​trúc khác nhau rất hữu ích.
Fearless_fool

1

Đây không phải là cách nhanh nhất hoặc ngắn nhất, nhưng tôi nghĩ nó rất dễ đọc. Vì vậy, tôi sẽ làm một cái gì đó như thế này:

bool is_power_of_2(int n)
  int bitCounter=0;
  while(n) {
    if ((n & 1) == 1) {
      ++bitCounter;
    }
    n >>= 1;
  }
  return (bitCounter == 1);
}

Điều này hoạt động vì hệ nhị phân dựa trên lũy thừa của hai. Bất kỳ số nào chỉ có một bit được đặt phải là lũy thừa của hai.


Nó có thể không nhanh hay ngắn, nhưng nó chính xác không giống như các câu trả lời hàng đầu.

2
Tại thời điểm bình luận, tất cả họ đều bị nghe trộm. Chúng đã được chỉnh sửa thành một trạng thái có thể chấp nhận được.

0

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

bool is_power_of_2(int x) {
    return x > 0 && (x<<1 == (x|(x-1)) +1));
}

0

Nó có thể thông qua c ++

int IsPowOf2(int z) {
double x=log2(z);
int y=x;
if (x==(double)y)
return 1;
else
return 0;
}

2
Điều đó không đơn giản, cũng không nhanh chóng đối với tôi.
luk32 14/09/15

2
Tức là nó chắc chắn không nhanh do log2và bằng chứng rằng nó hoạt động không dễ giải thích (chính xác là bạn có thể bị mắc lỗi làm tròn không?). Nó cũng không cần thiết phải phức tạp với if..return..else..return. Có gì sai khi thu gọn nó vào return x==(double)y;? Nó sẽ trả về boolanyayws. IMO thậm chí nhà điều hành bậc ba sẽ rõ ràng hơn nếu một người thực sự muốn gắn bó int.
luk32

0

Tôi biết đây là một bài viết rất cũ, nhưng tôi nghĩ nó có thể thú vị khi đăng bài này ở đây.


Từ Code-Golf SE (vì vậy tất cả ghi công cho (những) người đã viết bài này): Giới thiệu ngôn ngữ

(Đoạn về C , đoạn con Độ dài đoạn 36 )

bool isPow2(const unsigned int num){return!!num&!(num&(num-1));}

-1

Một cách khác để thực hiện (có thể không nhanh nhất) là xác định xem ln (x) / ln (2) có phải là một số nguyên hay không.


2
Có thể không có về nó :-).
paxdiablo 20/09/08

1
Điều này sẽ có vấn đề với sự thiếu chính xác của dấu chấm động. ln (1 << 29) / ln (2) ra 29.000000000000004.
Anonymous

-3

Đây là phương pháp dịch chuyển bit trong T-SQL (SQL Server):

SELECT CASE WHEN @X>0 AND (@X) & (@X-1)=0 THEN 1 ELSE 0 END AS IsPowerOfTwo

Nó nhanh hơn rất nhiều so với thực hiện một lôgarit bốn lần (lần đặt đầu tiên để nhận kết quả thập phân, bộ thứ hai để lấy bộ số nguyên và so sánh)


5
Thật tốt khi thấy câu trả lời hàng đầu cho câu hỏi này cũng có thể được triển khai như thế nào trong T-SQL, nhưng điều đó không thực sự liên quan đến câu hỏi được hỏi ở đây. Một giải pháp thay thế (nếu bạn đang tìm kiếm giải pháp trong T-SQL, tìm thấy câu hỏi đã trả lời này, triển khai nó trong T-SQL và nghĩ rằng nó đủ thú vị để đăng câu trả lời này) là đăng câu hỏi có tham chiếu đến T-SQL, sau đó tự trả lời nó, với tham chiếu đến câu hỏi đã trả lời này. Hy vọng gợi ý này là hữu ích.
Simon

cái này không thực sự trả lời câu hỏi nà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.