Hari ini saya membutuhkan algoritma sederhana untuk memeriksa apakah angka adalah kekuatan 2.
Algoritma tersebut harus:
- Sederhana
- Benar untuk
ulong
nilai apa pun .
Saya datang dengan algoritma sederhana ini:
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;
}
Tapi kemudian saya berpikir, bagaimana kalau mengecek apakah angka bulat persis? Tetapi ketika saya memeriksa 2 ^ 63 +1, dikembalikan persis 63 karena pembulatan. Jadi saya memeriksa apakah 2 pangkat 63 sama dengan angka asli - dan itu, karena perhitungan dilakukan dalam s dan bukan dalam angka pastinya:log2 x
Math.Log
double
private bool IsPowerOfTwo_2(ulong number)
{
double log = Math.Log(number, 2);
double pow = Math.Pow(2, Math.Round(log));
return pow == number;
}
Ini kembali true
untuk nilai yang salah yang diberikan: 9223372036854775809
.
Apakah ada algoritma yang lebih baik?
(x & (x - 1))
dapat mengembalikan positif palsu ketikaX
jumlah kekuatan dua, misalnya8 + 16
.Jawaban:
Ada trik sederhana untuk masalah ini:
Catatan, fungsi ini akan melaporkan
true
untuk0
, yang bukan merupakan kekuatan2
. Jika Anda ingin mengecualikan itu, berikut caranya:Penjelasan
Pertama dan terutama, biner & operator bitwise dari definisi MSDN:
Sekarang mari kita lihat bagaimana semua ini berlangsung:
Fungsi mengembalikan boolean (true / false) dan menerima satu parameter masuk bertipe unsigned long (x, dalam kasus ini). Mari kita demi kesederhanaan menganggap bahwa seseorang telah melewati nilai 4 dan memanggil fungsi seperti ini:
Sekarang kami mengganti setiap kemunculan x dengan 4:
Ya, kita sudah tahu bahwa 4! = 0 bernilai true, sejauh ini bagus. Tapi bagaimana dengan:
Ini diterjemahkan menjadi ini tentu saja:
Tapi apa sebenarnya itu
4&3
?Representasi biner dari 4 adalah 100 dan representasi biner dari 3 adalah 011 (ingat & mengambil representasi biner dari angka-angka ini). Jadi kita punya:
Bayangkan nilai-nilai ini ditumpuk seperti penambahan dasar. The
&
Operator mengatakan bahwa jika kedua nilai-nilai yang sama dengan 1 maka hasilnya adalah 1, jika tidak 0. Jadi1 & 1 = 1
,1 & 0 = 0
,0 & 0 = 0
, dan0 & 1 = 0
. Jadi kami menghitung:Hasilnya hanya 0. Jadi kita kembali dan melihat apa yang diterjemahkan oleh pernyataan pengembalian sekarang:
Yang diterjemahkan sekarang menjadi:
Kita semua tahu itu
true && true
sederhanatrue
, dan ini menunjukkan bahwa sebagai contoh kita, 4 adalah kekuatan 2.sumber
Beberapa situs yang mendokumentasikan dan menjelaskan ini dan peretasan twiddling lainnya adalah:
( http://graphics.stanford.edu/~seander/bithacks.html#DetermineIfPowerOf2 )
( http://bits.stephan-brumme.com/isPowerOfTwo.html )
Dan nenek moyang mereka, buku "Hacker's Delight" oleh Henry Warren, Jr .:
Seperti yang dijelaskan oleh halaman Sean Anderson , ungkapan yang
((x & (x - 1)) == 0)
salah mengindikasikan bahwa 0 adalah kekuatan 2. Ia menyarankan untuk menggunakan:untuk memperbaiki masalah itu.
sumber
!
hanya dapat diterapkan pada tipe boolean, dan&&
juga mengharuskan kedua operan menjadi boolean- (Kecuali operator yang ditentukan pengguna membuat hal-hal lain menjadi mungkin, tetapi itu tidak relevan untukulong
.)return (i & -i) == i
sumber
i
yang diatur juga akan ditetapkan-i
. Bit di bawah itu akan menjadi 0 (di kedua nilai) sedangkan bit di atasnya akan terbalik sehubungan satu sama lain. Nilaii & -i
karena itu akan menjadi bit set paling tidak signifikani
(yang merupakan kekuatan dua). Jikai
memiliki nilai yang sama maka itu hanya set bit. Gagal ketikai
0 untuk alasan yangi & (i - 1) == 0
sama.i
merupakan tipe yang tidak ditandatangani, dua pelengkap tidak ada hubungannya dengan itu. Anda hanya mengambil keuntungan dari sifat-sifat aritmatika modular dan bitwise dan.i==0
(mengembalikan(0&0==0)
yang adatrue
). Seharusnyareturn i && ( (i&-i)==i )
sumber
Saya menulis artikel tentang ini baru-baru ini di http://www.exploringbinary.com/ten-ways-to-check-if-an-integer-is-a-power-of-two-in-c/ . Ini mencakup penghitungan bit, cara menggunakan logaritma dengan benar, centang klasik "x &&! (X & (x - 1))", dan lainnya.
sumber
Inilah solusi C ++ sederhana :
sumber
__builtin_popcount
. Sayangnya, satu keluarga prosesor belum memiliki instruksi perakitan tunggal untuk melakukan ini (x86), jadi itu adalah metode tercepat untuk penghitungan bit. Pada arsitektur lain ini adalah instruksi perakitan tunggal.popcnt
Adendum berikut untuk jawaban yang diterima mungkin bermanfaat bagi sebagian orang:
Kekuatan dua, ketika diekspresikan dalam biner, akan selalu terlihat seperti 1 diikuti oleh n nol di mana n lebih besar dari atau sama dengan 0. Ex:
dan seterusnya.
Ketika kita kurangi
1
dari angka-angka ini, angka itu menjadi 0 diikuti oleh n dan lagi sama seperti di atas. Ex:dan seterusnya.
Datang ke inti
Yang
x
disejajarkan dengan nolx - 1
dan semua nolx
disejajarkan dengan yangx - 1
, menyebabkan bitwise DAN menghasilkan 0. Dan itulah bagaimana kita memiliki jawaban satu baris yang disebutkan di atas benar.Lebih lanjut menambah keindahan jawaban yang diterima di atas -
Jadi, kami memiliki properti yang dapat kami miliki sekarang:
Salah satu penggunaan luar biasa dari properti ini adalah mencari tahu - Berapa banyak 1 yang hadir dalam representasi biner dari angka yang diberikan? Kode pendek dan manis untuk melakukan itu untuk bilangan bulat yang diberikan
x
adalah:Aspek lain dari angka yang dapat dibuktikan dari konsep yang dijelaskan di atas adalah "Bisakah setiap angka positif direpresentasikan sebagai jumlah dari kekuatan 2?" .
Ya, setiap angka positif dapat direpresentasikan sebagai jumlah kekuatan 2. Untuk angka apa pun, ambil representasi binernya. Contoh: Ambil nomor
117
.sumber
Setelah memposting pertanyaan saya memikirkan solusi berikut:
Kita perlu memeriksa apakah tepat satu dari angka biner itu satu. Jadi kita cukup menggeser angka yang benar satu digit pada satu waktu, dan kembali
true
jika sama dengan 1. Jika pada titik mana pun kita datang dengan angka ganjil ((number & 1) == 1
), kita tahu hasilnyafalse
. Ini terbukti (menggunakan tolok ukur) sedikit lebih cepat daripada metode asli untuk nilai benar (besar) dan jauh lebih cepat untuk nilai palsu atau kecil.Tentu saja, solusi Greg jauh lebih baik.
sumber
Dan inilah algoritma umum untuk mengetahui apakah suatu angka adalah kekuatan dari nomor lain.
sumber
sumber
c#
? Saya kira inic++
sepertix
dikembalikan sebagai bool.Cari apakah angka yang diberikan adalah kekuatan 2.
sumber
frexp
daripadalog
hal - hal buruk jika Anda ingin menggunakan floating point.sumber
Ini sangat cepat. Diperlukan sekitar 6 menit dan 43 detik untuk memeriksa semua 2 ^ 32 bilangan bulat.
sumber
Jika
x
kekuatan dua, satu-satunya 1 bit berada di posisin
. Ini berartix – 1
memiliki 0 pada posisin
. Untuk melihat alasannya, ingat cara kerja pengurangan biner. Ketika mengurangi 1 darix
, pinjaman menyebar ke posisin
; bitn
menjadi 0 dan semua bit lebih rendah menjadi 1. Sekarang, karenax
tidak memiliki 1 bit yang sama denganx – 1
,x & (x – 1)
adalah 0, dan!(x & (x – 1))
itu benar.sumber
Angka adalah kekuatan 2 jika hanya berisi 1 set bit. Kita dapat menggunakan properti ini dan fungsi generik
countSetBits
untuk menemukan apakah angka berkekuatan 2 atau tidak.Ini adalah program C ++:
Kita tidak perlu memeriksa secara eksplisit untuk 0 sebagai Kekuatan 2, karena mengembalikan False untuk 0 juga.
KELUARAN
sumber
while
bukanif
? Saya pribadi tidak dapat melihat alasannya, tetapi tampaknya itu akan berhasil. EDIT: - tidak ... itu akan mengembalikan 1 untuk sesuatu yang lebih besar dari0
!?Berikut adalah metode lain yang saya buat, dalam hal ini menggunakan
|
alih-alih&
:sumber
(x > 0)
sedikit di sini?untuk kekuatan 2, yang berikut ini juga berlaku.
n & (- n) == n
CATATAN: gagal untuk n = 0, jadi perlu memeriksanya
Alasan mengapa ini bekerja adalah:
-n adalah komplemen 2s dari n. -n akan memiliki setiap bit di sebelah kiri set bit paling kanan dari n membalik dibandingkan dengan n. Untuk kekuatan 2 hanya ada satu set bit.
sumber
Contoh
Algoritma
Menggunakan bit mask, bagi
NUM
variabel dalam binerIF R > 0 AND L > 0: Return FALSE
Kalau tidak,
NUM
jadilah yang bukan nolIF NUM = 1: Return TRUE
Jika tidak, lanjutkan ke Langkah 1
Kompleksitas
Waktu ~ di
O(log(d))
manad
jumlah digit binersumber
Meningkatkan jawaban @ user134548, tanpa aritmatika bit:
Ini berfungsi dengan baik untuk:
sumber
Mark gravell menyarankan ini jika Anda memiliki .NET Core 3, System.Runtime.Intrinsics.X86.Popcnt.PopCount
Instruksi tunggal, lebih cepat dari
(x != 0) && ((x & (x - 1)) == 0)
tetapi kurang portable.sumber
(x != 0) && ((x & (x - 1)) == 0)
? Saya ragu itu, esp. pada sistem lama di mana popcnt tidak tersediaDi C, saya menguji
i && !(i & (i - 1)
trik dan membandingkannya dengan__builtin_popcount(i)
, menggunakan gcc di Linux, dengan flag -mpopnt untuk memastikan untuk menggunakan instruksi POPCNT CPU. Program pengujian saya menghitung # bilangan bulat antara 0 dan 2 ^ 31 yang merupakan kekuatan dua.Pada awalnya saya pikir
i && !(i & (i - 1)
itu 10% lebih cepat, meskipun saya memverifikasi bahwa POPCNT digunakan dalam pembongkaran di mana saya menggunakan__builtin_popcount
.Namun, saya menyadari bahwa saya telah memasukkan pernyataan if, dan prediksi cabang mungkin lebih baik pada versi yang sedikit memutar-mutar. Saya menghapus if dan POPCNT berakhir lebih cepat, seperti yang diharapkan.
Hasil:
Intel (R) Core (TM) i7-4771 CPU maks 3.90GHz
AMD Ryzen Threadripper 2950X 16-Core Processor maks 3.50GHz
Perhatikan bahwa di sini CPU Intel tampaknya sedikit lebih lambat dari AMD dengan sedikit memutar-mutar, tetapi memiliki POPCNT yang jauh lebih cepat; AMD POPCNT tidak memberikan banyak dorongan.
popcnt_test.c:
Jalankan tes:
sumber
Saya melihat banyak jawaban menyarankan untuk mengembalikan n &&! (N & (n - 1)) tetapi menurut pengalaman saya jika nilai input negatif, ia mengembalikan nilai palsu. Saya akan membagikan pendekatan sederhana lain di sini karena kita tahu kekuatan dua angka hanya memiliki satu set bit, jadi cukup kita akan menghitung jumlah bit set, ini akan membutuhkan waktu O (log N).
Periksa artikel ini untuk menghitung no. bit set
sumber
sumber
Program ini di java mengembalikan "true" jika angka adalah kekuatan 2 dan mengembalikan "false" jika bukan kekuatan 2
sumber