Apa cara paling sederhana untuk menguji apakah suatu bilangan adalah pangkat 2 di C ++?


94

Saya butuh fungsi seperti ini:

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

Adakah yang bisa menyarankan bagaimana saya bisa menulis ini? Dapatkah Anda memberi tahu saya situs web yang bagus tempat algoritme semacam ini dapat ditemukan?



@rootTraveller - Mungkin bukan duplikat. C ++ dan Java adalah bahasa yang berbeda dan masing-masing menawarkan fasilitas yang berbeda. Misalnya, Di C / C ++ sekarang kita dapat menggunakan intrinsik dengan prosesor yang mendukung BMI, yang mengeluarkan instruksi mesin untuk melakukannya dalam satu jam. Saya membayangkan Java memiliki hal-hal lain, seperti mungkin sesuatu dari rutinitas Matematika.
jww

Jawaban:


191

(n & (n - 1)) == 0paling baik. Namun, perhatikan bahwa ini akan salah mengembalikan nilai true untuk n = 0, jadi jika memungkinkan, Anda ingin memeriksanya secara eksplisit.

http://www.graphics.stanford.edu/~seander/bithacks.html memiliki banyak koleksi algoritme bit-twiddling yang cerdas, termasuk yang ini.


8
jadi pada dasarnya(n>0 && ((n & (n-1)) == 0))
Saurabh Goyal

1
@SaurabhGoyal atau n && !(n & (n - 1))sebagai tautan dalam pernyataan jawaban.
Carsten

Mengapa, oh mengapa, bukankah ini ada di bagian atas jawaban? OP mohon terima.
donturner

@SaurabhGoyal Salah satu perbaikan kecil ini: n & !(n & (n - 1)). Perhatikan bitwise AND &(tidak logis dan &&). Operator bitwise tidak mengimplementasikan korsleting dan, karenanya, kodenya tidak bercabang. Hal ini lebih disukai dalam situasi di mana kesalahan prediksi cabang mungkin terjadi dan saat menghitung rhs ekspresi (yaitu, !(n & (n - 1))) itu murah.
Cassio Neri

@cassio !adalah operator logika dan karenanya nilai !(n & (n - 1))akan menjadi boolean, Apakah Anda yakin boolean dan nomor dapat diberikan ke operator DAN bitwise? Jika ya, itu terlihat bagus.
Saurabh Goyal

81

Sebuah kekuatan dua hanya akan memiliki satu set bit (untuk nomor yang tidak bertanda tangan). Sesuatu seperti

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

Akan bekerja dengan baik; satu kurang dari pangkat dua adalah semua 1 dalam bit kurang signifikan, jadi harus DAN 0 bitwise.

Karena saya mengasumsikan angka yang tidak ditandatangani, tes == 0 (yang awalnya saya lupa, maaf) sudah memadai. Anda mungkin menginginkan tes> 0 jika Anda menggunakan bilangan bulat bertanda.


Anda melewatkan '!' atau '== 0'

Anda juga melewatkan tes untuk nilai negatif x.
Rob Wells

Baik, bagaimana Anda mengeditnya tanpa 'diedit x menit yang lalu' muncul?

Sungguh, bagaimana Anda bisa mendapatkan 120 repetisi untuk jawaban yang salah?

@ Mike F: Memang, tampaknya orang akan memberikan suara pada jawaban tanpa memeriksanya. Siapa pun dapat membuat kesalahan, saya kira - jika saya membuat kesalahan di masa mendatang, silakan mengeditnya.
Adam Wright

49

Pangkat dua dalam biner terlihat seperti ini:

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

Perhatikan bahwa selalu ada 1 bit set. Satu-satunya pengecualian adalah dengan bilangan bulat yang ditandatangani. Misalnya, bilangan bulat bertanda 8-bit dengan nilai -128 terlihat seperti ini:

10000000

Jadi setelah memeriksa bahwa angkanya lebih besar dari nol, kita dapat menggunakan sedikit retasan pintar untuk menguji bahwa satu dan hanya satu bit yang ditetapkan.

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

Untuk sedikit bermain-main, lihat di sini .


14

Pendekatan # 1:

Bagilah angka dengan 2 secara berturut-turut untuk memeriksanya.

Kompleksitas waktu: O (log2n).

Pendekatan # 2:

Bitwise AND angka dengan angka sebelumnya harus sama dengan NOL.

Contoh: Angka = 8 Biner 8: 1 0 0 0 Biner 7: 0 1 1 1 dan bitwise AND dari kedua bilangan tersebut adalah 0 0 0 0 = 0.

Kompleksitas waktu: O (1).

Pendekatan # 3:

Bitwise XOR angka dengan angka sebelumnya harus merupakan jumlah dari kedua angka.

Contoh: Bilangan = 8 Biner 8: 1 0 0 0 Biner 7: 0 1 1 1 dan XOR bitwise dari kedua bilangan tersebut adalah 1 1 1 1 = 15.

Kompleksitas waktu: 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

untuk pangkat 2 apa pun, berikut ini juga berlaku.

n & (- n) == n

CATATAN: Kondisi ini benar untuk n = 0, meskipun itu bukan pangkat 2.
Alasan mengapa ini berhasil adalah:
-n adalah komplemen 2s dari n. -n akan memiliki setiap bit di sebelah kiri set bit paling kanan dari n dibalik dibandingkan dengan n. Untuk pangkat 2 hanya ada satu set bit.


2
Maksud saya kondisinya benar untuk n = 0 meskipun itu bukan kekuatan dua
FReeze FRancis

apakah ini berhasil dengan konversi yang terjadi jika n unsigned?
Joseph Garvin

6

Ini mungkin yang tercepat, jika menggunakan GCC. Ini hanya menggunakan instruksi cpu POPCNT dan satu perbandingan. Representasi biner dari setiap pangkat 2 angka, selalu hanya satu set bit, bit lain selalu nol. Jadi kami menghitung jumlah bit set dengan POPCNT, dan jika itu sama dengan 1, jumlahnya adalah pangkat 2. Saya rasa tidak ada metode yang lebih cepat. Dan itu sangat sederhana, jika Anda memahaminya sekali:

if(1==__builtin_popcount(n))

Nggak. Saya baru saja menguji ini. Saya suka popcount tetapi untuk pengujian power-of-2, pengujiannya i && !(i & (i - 1)))sekitar 10% lebih cepat pada mesin saya, bahkan ketika saya yakin untuk mengaktifkan instruksi POPCNT assembly asli di gcc.
eraoul

Ups, saya ambil kembali. Program pengujian saya berjalan dalam putaran dan prediksi cabang "curang". Anda benar, jika Anda memiliki instruksi POPCNT di CPU Anda, itu lebih cepat.
eraoul

5

Di C ++ 20 ada std::ispow2yang dapat Anda gunakan untuk tujuan ini jika Anda tidak perlu mengimplementasikannya sendiri:

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

3

Mengikuti akan lebih cepat daripada jawaban yang paling banyak dipilih karena hubungan pendek boolean dan fakta bahwa perbandingannya lambat.

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

Jika Anda tahu bahwa x tidak bisa 0 maka

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


3

Apa cara paling sederhana untuk menguji apakah suatu bilangan adalah pangkat 2 di C ++?

Jika Anda memiliki prosesor Intel modern dengan Petunjuk Manipulasi Bit , Anda dapat melakukan hal berikut. Ini menghilangkan kode C / C ++ langsung karena orang lain telah menjawabnya, tetapi Anda memerlukannya jika BMI tidak tersedia atau diaktifkan.

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
}

GCC, ICC, dan Clang memberi sinyal dukungan BMI dengan __BMI__. Ini tersedia di kompiler Microsoft di Visual Studio 2015 dan di atasnya saat AVX2 tersedia dan diaktifkan . Untuk header yang Anda perlukan, lihat File header untuk intrinsik SIMD .

Saya biasanya menjaga _blsr_u64dengan _LP64_jika kompilasi pada i686. Clang memerlukan sedikit solusi karena menggunakan simbol intrinsik nam yang sedikit berbeda:

#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

Dapatkah Anda memberi tahu saya situs web yang bagus di mana algoritme semacam ini dapat ditemukan?

Situs web ini sering dikutip: Bit Twiddling Hacks .


Ini tentu bukan "cara termudah" seperti yang diminta di OP, tapi bisa dibilang paling cepat untuk lingkungan tertentu. Menunjukkan cara membuat persyaratan untuk arsitektur yang berbeda sangatlah berguna.
fearless_fool

1

Ini bukan cara tercepat atau terpendek, tapi menurut saya ini sangat mudah dibaca. Jadi saya akan melakukan sesuatu seperti ini:

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

Ini berfungsi karena biner didasarkan pada pangkat dua. Setiap angka dengan hanya satu set bit harus menjadi pangkat dua.


Ini mungkin tidak cepat atau pendek, tapi benar tidak seperti jawaban teratas.

2
Saat memberi komentar, mereka semua disadap. Mereka sejak itu telah diedit menjadi kondisi yang dapat diterima.

0

Berikut adalah metode lain, dalam hal ini menggunakan |sebagai pengganti &:

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

0

Ini dimungkinkan melalui c ++

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

2
Itu tidak sederhana, tidak juga cepat, bagi saya.
luk32

2
Yaitu itu tentu tidak cepat karena log2, dan bukti bahwa itu berhasil tidak begitu mudah untuk dijelaskan (tepatnya, bisakah Anda terjebak oleh kesalahan pembulatan?). Ini juga tidak perlu berbelit-belit if..return..else..return. Apa salahnya dengan menciutkannya return x==(double)y;? Ini harus mengembalikan boolanyayws. Bahkan operator terner IMO akan lebih jelas jika seseorang benar-benar ingin berpegang teguh int.
Lukas32

0

Saya tahu ini adalah posting yang sangat lama, tetapi saya pikir akan menarik untuk memposting ini di sini.


Dari Code-Golf SE (jadi semua kredit kepada orang-orang yang menulis ini): Showcase of Languages

(Paragraph about C , subparagraph Length 36 snippet )

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

-1

Cara lain untuk pergi (mungkin bukan tercepat) adalah dengan menentukan apakah ln (x) / ln (2) adalah bilangan bulat.


2
Tidak mungkin tentang itu :-).
paxdiablo

1
Ini akan memiliki masalah dengan ketidakakuratan floating point. ln (1 << 29) / ln (2) keluar menjadi 29.000000000000004.
Anonim

-3

Ini adalah metode bit-shift di T-SQL (SQL Server):

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

Ini jauh lebih cepat daripada melakukan logaritma empat kali (set pertama untuk mendapatkan hasil desimal, set kedua untuk mendapatkan set integer & bandingkan)


5
Sangat bagus untuk melihat bagaimana jawaban teratas untuk pertanyaan ini juga dapat diterapkan di T-SQL, tetapi itu tidak terlalu relevan dengan pertanyaan yang ditanyakan di sini. Sebuah alternatif (jika Anda mencari solusi dalam T-SQL, menemukan pertanyaan terjawab ini, menerapkannya dalam T-SQL dan merasa cukup menarik untuk memposting jawaban ini) adalah dengan memposting pertanyaan dengan mengacu pada T-SQL, lalu jawablah sendiri, dengan mengacu pada pertanyaan terjawab ini. Semoga saran ini bermanfaat.
Simon

ini tidak benar-benar menjawab pertanyaan ini
phuclv
Dengan menggunakan situs kami, Anda mengakui telah membaca dan memahami Kebijakan Cookie dan Kebijakan Privasi kami.
Licensed under cc by-sa 3.0 with attribution required.