C ++ 'da bir sayının 2'nin üssü olup olmadığını test etmenin en basit yolu nedir?


94

Bunun gibi bir işleve ihtiyacım var:

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

Bunu nasıl yazabileceğimi kimse önerebilir mi? Bana bu tür bir algoritmanın bulunabileceği iyi bir web sitesi söyleyebilir misiniz?



@rootTraveller - Muhtemelen bir kopya değil. C ++ ve Java farklı dillerdir ve her biri farklı olanaklar sunar. Örneğin, C / C ++ 'da artık bir saatte bunu yapmak için makine talimatını veren BMI etkin işlemcilerle içsel bilgileri kullanabiliriz. Java'nın başka şeyler de olduğunu düşünüyorum, mesela bir Matematik rutini gibi.
jww

Yanıtlar:


191

(n & (n - 1)) == 0en iyisi. Bununla birlikte, n = 0 için yanlış olarak doğru sonucunu döndüreceğini unutmayın, bu nedenle eğer bu mümkünse, açıkça kontrol etmek isteyeceksiniz.

http://www.graphics.stanford.edu/~seander/bithacks.html , bunun dahil olmak üzere akıllı bit döndürme algoritmalarından oluşan geniş bir koleksiyona sahiptir.


8
temelde(n>0 && ((n & (n-1)) == 0))
Saurabh Goyal

1
@SaurabhGoyal veya n && !(n & (n - 1))yanıt durumları içindeki bağlantı olarak.
Carsten

Neden, ah neden, bu cevapların başında değil? OP lütfen kabul edin.
donturner

@SaurabhGoyal Bir küçük gelişme şudur: n & !(n & (n - 1)). Bitsel AND'ye dikkat edin &(mantıksal ve değil &&). Bitsel operatörler kısa devre yapmazlar ve bu nedenle kod dallanmaz. Bu, dal yanlış tahminlerinin muhtemel olduğu ve ifadenin rhs'sini hesaplarken (yani !(n & (n - 1))) ucuz olduğu durumlarda tercih edilir .
Cassio Neri

@cassio !mantıksal bir operatördür ve dolayısıyla değeri !(n & (n - 1))bir boole olacaktır. Bir boole ve bir sayının bitsel AND operatörüne verilebileceğinden emin misiniz? Evet ise, iyi görünüyor.
Saurabh Goyal

81

İkinin kuvveti sadece bir bit setine sahip olacaktır (işaretsiz sayılar için). Gibi bir şey

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

İyi çalışacak; ikinin kuvvetinden küçük bir, daha az anlamlı bitlerdeki tüm 1'lerdir, bu nedenle AND'den 0'a kadar olmalıdır.

İmzasız sayıları varsaydığım için, == 0 testi (başlangıçta unuttuğum, üzgünüm) yeterli. İşaretli tam sayılar kullanıyorsanız> 0 testi isteyebilirsiniz.


Eksik bir '!' veya '== 0'

Ayrıca negatif x değeri için bir testi kaçırıyorsunuz.
Rob Wells

Neat, 'x dakika önce düzenlendi' görünmeden nasıl düzenlediniz?

Cidden, bariz bir şekilde yanlış bir cevap için nasıl 120 tekrar aldın?

@Mike F: Gerçekten de insanlar cevapları kontrol etmeden oylayacak gibi görünüyor. Herkes bir hata yapabilir, sanırım - gelecekte bir hata yaparsam, bunları düzenlemekten çekinmeyin.
Adam Wright

49

İkili durumdaki ikinin güçleri şuna benzer:

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

Her zaman tam olarak 1 bit set olduğunu unutmayın. Tek istisna, işaretli bir tamsayıdır. örneğin -128 değerine sahip 8 bitlik işaretli bir tam sayı şuna benzer:

10000000

Dolayısıyla, sayının sıfırdan büyük olduğunu kontrol ettikten sonra, bir bitin ve yalnızca bir bitin ayarlandığını test etmek için akıllıca küçük bir hack kullanabiliriz.

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

Daha fazla oynatma için buraya bakın .


14

Yaklaşım 1:

Kontrol etmek için sayıyı 2'ye bölün.

Zaman karmaşıklığı: O (log2n).

Yaklaşım # 2:

Bit düzeyinde VE önceki numarasıyla birlikte sayı SIFIR'a eşit olmalıdır.

Örnek: Sayı = 8 8'in ikilisi: 1 0 0 0 7'nin ikilisi: 0 1 1 1 ve her iki sayının bitsel AND'si 0 0 0 0 = 0'dır.

Zaman karmaşıklığı: O (1).

Yaklaşım 3:

Bit tabanlı ÖZELVEYA, önceki numarasıyla birlikte sayı her iki sayının toplamı olmalıdır.

Örnek: Sayı = 8 8'in ikilisi: 1 0 0 0 7'nin ikilisi: 0 1 1 1 ve her iki sayının bitsel XOR değeri 1 1 1 1 = 15'tir.

Zaman karmaşıklığı: 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

2'nin herhangi bir kuvveti için aşağıdakiler de geçerlidir.

n & (- n) == n

NOT: Koşul n = 0 için doğrudur, ancak 2'nin kuvveti değildir.
Bunun işe yaramasının nedeni:
-n, n'nin 2s tümleyidir. -n, n'ye kıyasla en sağdaki n bitinin solundaki her bitine sahip olacaktır. 2'nin kuvvetleri için sadece bir set biti vardır.


2
koşulun n = 0 için doğru olduğunu kastetmiştim, ancak bunun gücü iki değil
FReeze FRancis

bu, n işaretsiz ise meydana gelen dönüşümlerle mi çalışır?
Joseph Garvin

6

GCC kullanılıyorsa, bu muhtemelen en hızlısıdır. Yalnızca POPCNT cpu komutu ve bir karşılaştırma kullanır. 2 sayının herhangi bir kuvvetinin ikili gösterimi, her zaman sadece bir bit kümesine sahiptir, diğer bitler her zaman sıfırdır. Bu yüzden POPCNT ile set bitlerinin sayısını sayıyoruz ve eğer 1'e eşitse, sayı 2'nin üssüdür. Daha hızlı bir yöntem olduğunu düşünmüyorum. Ve bir kez anladıysanız çok basit:

if(1==__builtin_popcount(n))

Hayır! Bunu daha yeni test ettim. Popcount'u seviyorum ama power-of-2 testi için, i && !(i & (i - 1)))gcc'de yerel derleme POPCNT komutunu etkinleştirdiğimden emin olsam bile , test makinemde yaklaşık% 10 daha hızlı.
periodoul

Oops geri alıyorum. Test programım bir döngü içinde çalışıyordu ve dallanma tahmini "hile" idi. Haklısın, CPU'nuzda POPCNT talimatı varsa daha hızlıdır.
periodoul

5

C ++ 20'de, std::ispow2kendiniz uygulamanız gerekmiyorsa, tam olarak bu amaç için kullanabileceğiniz bir şey vardır:

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

3

Boolean kısa devre ve karşılaştırmanın yavaş olduğu gerçeği nedeniyle takip etmek, en çok oylanan cevaptan daha hızlı olacaktır.

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

X'in 0 olamayacağını biliyorsanız

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


3

C ++ 'da bir sayının 2'nin üssü olup olmadığını test etmenin en basit yolu nedir?

Bit Manipülasyon Talimatlarına sahip modern bir Intel işlemciniz varsa, aşağıdakileri gerçekleştirebilirsiniz. Başkaları zaten yanıtladığı için düz C / C ++ kodunu atlar, ancak BMI mevcut değilse veya etkin değilse buna ihtiyacınız vardır.

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 ve Clang sinyali BMI desteği __BMI__. AVX2 kullanılabilir ve etkinleştirildiğinde , Visual Studio 2015 ve sonraki sürümlerde Microsoft derleyicilerinde kullanılabilir . İhtiyaç duyduğunuz başlıklar için bkz . SIMD içselleri için başlık dosyaları .

Genellikle i686'da derleyen _blsr_u64bir _LP64_in case ile korurum. Clang'ın biraz farklı bir iç sembol adı kullandığı için biraz geçici çözüme ihtiyacı var:

#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

Bana bu tür bir algoritmanın bulunabileceği iyi bir web sitesi söyleyebilir misiniz?

Bu web sitesinde sık sık alıntı yapılır: Bit Twiddling Hacks .


Bu, OP'de talep edildiği gibi kesinlikle "en basit yol" değildir, ancak belirli ortamlar için tartışmasız en hızlısıdır. Farklı mimariler için nasıl koşullu hale getirileceğini göstermek son derece yararlıdır.
fearless_fool

1

Bu en hızlı veya en kısa yol değil ama bence çok okunabilir. Bu yüzden şöyle bir şey yapardım:

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

İkili ikinin kuvvetlerine dayandığından bu işe yarar. Yalnızca bir bit kümesine sahip herhangi bir sayı ikinin üssü olmalıdır.


Hızlı veya kısa olmayabilir, ancak en çok sorulan cevapların aksine doğrudur.

2
Yorum yaparken hepsi dinleniyordu. O zamandan beri kabul edilebilir bir duruma getirildiler.

0

İşte bu durumda |yerine kullanan başka bir yöntem &:

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

0

C ++ ile mümkündür

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

2
Bu benim için ne basit ne de hızlı.
luk32

2
Yani kesinlikle hızlı değil log2ve işe yaradığının kanıtı açıklamak o kadar kolay değil (tam olarak, hataları yuvarlayarak yakalanabilir misiniz?). Ayrıca gereksiz yere kıvrılmış if..return..else..return. Onu çökertmenin nesi yanlış return x==(double)y;? Her boolhalükarda geri dönmelidir. Biri gerçekten bağlı kalmak istiyorsa, IMO üçlü operatörü bile daha net olacaktır int.
luk32

0

Bunun çok eski bir gönderi olduğunu biliyorum , ancak bunu buraya göndermenin ilginç olabileceğini düşündüm.


Gönderen Kod-Golf SE (yani bu yazmış biri (ler) için tüm kredi): Diller Vitrini

( C ile ilgili paragraf, Uzunluk 36 pasajı )

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

-1

Başka bir yol (belki de en hızlı değil), ln (x) / ln (2) 'nin tam sayı olup olmadığını belirlemektir.


2
Bunun hakkında belki bir şey yok :-).
paxdiablo

1
Bunun kayan nokta yanlışlığı ile ilgili sorunları olacaktır. ln (1 << 29) / ln (2) 29.000000000000004'e çıkıyor.
Anonim

-3

Bu, T-SQL'deki (SQL Server) bit kaydırma yöntemidir:

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

Dört kez logaritma yapmaktan çok daha hızlıdır (ilk önce ondalık sonuç almak için, 2. tamsayı ayarlamak ve karşılaştırmak için set)


5
Bu sorunun en iyi cevabının T-SQL'de nasıl uygulanabileceğini görmek güzel, ancak bu burada sorulan soruyla pek alakalı değil. Bir alternatif (T-SQL'de bir çözüm arıyorsanız, bu cevaplanmış soruyu bulmuşsanız, T-SQL'de uyguladıysanız ve bu cevabı gönderecek kadar ilginç olduğunu düşündüyseniz), soruyu T-SQL'e referansla göndermek olacaktır, o zaman bu cevaplanan soruyu referans alarak kendiniz cevaplayın. Umarım bu öneri yardımcı olur.
Simon

bu gerçekten bu soruyu cevaplamıyor
phuclv
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.