数値が2の累乗であるかどうかを確認する方法


585

今日、数値が2のべき乗であるかどうかをチェックするための単純なアルゴリズムが必要でした。

アルゴリズムは:

  1. シンプルな
  2. ulong値を修正します。

私はこの簡単なアルゴリズムを思いつきました:

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

しかし、私は考えました、正確に丸い数であるかどうかをチェックするのはどうですか?しかし、2 ^ 63 + 1をチェックすると、丸めのために正確に63が返されました。したがって、2の累乗63が元の数と等しいかどうかを確認しました。これは、計算が正確な数ではなくsで行われるためです。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;
}

これはtrue、指定された誤った値に対して返されました:9223372036854775809

より良いアルゴリズムはありますか?


1
が2の累乗の合計である(x & (x - 1))場合、ソリューションは誤検知を返す可能性があると思います。X8 + 16
ジョーブラウン

32
すべての数値は2の累乗の和として記述できます。そのため、任意の数値を2進数で表すことができます。さらに、あなたの例では、偽陽性を返しません11000&10111理由= 10000 = 0!
VLSD

1
@JoeBrown誤検知はありません。実際、式は2の2の累乗の合計のうち大きい方を返します。
Samy Bencherif 2018

回答:


1220

この問題には簡単なトリックがあります:

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

この関数は報告し、注意trueのため0のパワーされていません、2。それを除外したい場合は、次のようにします。

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

説明

何よりもまず、MSDN定義のビット単位のバイナリと演算子:

バイナリ&演算子は、整数型とブールに対して事前定義されています。整数型の場合、&はそのオペランドのビットごとの論理ANDを計算します。boolオペランドの場合、&はそのオペランドの論理ANDを計算します。つまり、両方のオペランドがtrueの場合にのみ、結果がtrueになります。

これがどのように機能するかを見てみましょう:

この関数はブール値(true / false)を返し、unsigned longタイプ(この場合はx)の着信パラメーターを1つ受け入れます。簡単にするために、誰かが値4を渡し、そのように関数を呼び出したと仮定します。

bool b = IsPowerOfTwo(4)

次に、xの各出現箇所を4に置き換えます。

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

4!= 0の評価がtrueであることは既にわかっています。しかし、どうですか:

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

これはもちろんこれに変換されます:

((4 & 3) == 0)

しかし、正確には4&3何ですか?

4のバイナリ表現は100で、3のバイナリ表現は011です(&はこれらの数値のバイナリ表現をとることを忘れないでください)。だから私たちは:

100 = 4
011 = 3

これらの値が基本加算のように積み上げられていると想像してください。&オペレータは、それ以外の場合は0だからであり、両方の値が1に等しい場合、結果が1であると言う1 & 1 = 11 & 0 = 00 & 0 = 0、および0 & 1 = 0。だから私たちは計算をします:

100
011
----
000

結果は単純に0です。そこで、戻って、returnステートメントが次のように変換する内容を確認します。

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

これは次のように変換されます:

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

私たちは皆、それtrue && trueが単にtrueであることを知っています。これは、この例では、4が2の累乗であることを示しています。


56
@Kripp:数値はバイナリ形式の1000 ... 000になります。-1にすると、0111 ... 111の形式になります。したがって、2つの数のバイナリともたらすことたとえば1010100は、(...続き)で得られた、1010011となるので、これは、非パワー・オブ・補数のために発生しないであろう000000である
コンフィギュレータ

47
...バイナリの後に1010000となる。唯一の誤検知は0になるため、次のように使用します。return(x!= 0)&&((x&(x-1))== 0);
コンフィギュレー

6
クリップ、検討してください(2:1、10:1)(4:3、100:11)(8:7、1000:111)(16:15、10000:1111)パターンを参照してください?
Thomas L Holaday、

13
@ShuggyCoUk:2の補数は負の数がどのように表されるかを示します。これは符号なし整数であるため、負の数の表現は関係ありません。この手法は、負でない整数のバイナリ表現にのみ依存しています。
グレッグヒューギル

4
@SoapBox-より一般的なものは何ですか?2の累乗ではないゼロまたはゼロ以外の数値?これは、もう少しコンテキストがなければ答えられない質問です。そして、それは本当に、とにかく問題でありません。
コンフィギュレー

97

これと他のちょっとしたハッキン​​グを文書化して説明するいくつかのサイトは次のとおりです:

そして、それらの祖父、ヘンリー・ウォーレン・ジュニアによる本「ハッカーの喜び」

ショーン・アンダーソンのページには、説明、表現が((x & (x - 1)) == 0)間違って0に彼が使用することを示唆して2のべき乗であることを示しています。

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

その問題を修正します。


4
0は2の累乗です... 2 ^ -inf = 0。;););)
Michael Bray

4
これはC#タグ付きスレッドなので!、ブール型にのみ適用でき、&&両方のオペランドがブール型である必要があるため、(Sean Andersonの)最後の式はC#では不正であることを指摘する価値があります(ユーザー定義の演算子を除く)他のことを可能にしますが、それはulong。には関係ありません。)
Jeppe Stig Nielsen 2018

40

return (i & -i) == i


2
なぜこれが機能するのか、機能しないのか、何かヒントはありますか?私はその正しさをJavaのみでチェックしました。それが正しければ、これは優れた答えです。より速く+より小さく
Andreas Petersson

7
2の補数表記のプロパティの1つを利用します。数値の負の値を計算するには、ビットごとの否定を実行し、結果に1を追加します。最下位ビットiが設定されているものもで設定され-iます。その下のビットは0(両方の値で)になりますが、その上のビットは互いに反転します。i & -iしたがって、の値は、最下位セットビットi(2の累乗)になります。場合はi、同じ値を持つ唯一のビットがセットされたもの。i同じ理由でが0の場合は失敗i & (i - 1) == 0します。
マイケルカーマン

6
iが符号なしの型の場合、2の補数はそれとは関係ありません。あなたは単にモジュラー算術とビット単位のプロパティを利用しています。
R .. GitHub ICE HELPING ICEの停止

2
これは機能しませんi==0(0&0==0)どちらが返されるかtrue)。それはする必要がありますreturn i && ( (i&-i)==i )
bobobobo

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

3
この解決策は、負数が渡された場合に負数も処理できるため、より優れています。(ulongではなく長い場合)
Steven

この場合、小数が2のべき乗として渡されるのはなぜですか?
クリスFrisina


17

簡単なC ++ソリューションを次に示します。

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

8
gccでは、これはと呼ばれる単一のgcc組み込みにコンパイルされ__builtin_popcountます。残念ながら、1つのプロセッサフ​​ァミリには、これを実行するための単一のアセンブリ命令(x86)がまだありません。そのため、ビットカウントの最速の方法です。他のアーキテクチャでは、これは単一のアセンブリ命令です。
deft_code

3
@deft_code新しいx86マイクロアーキテクチャーのサポートpopcnt
phuclv 2017年

13

受け入れられた回答に対する次の補遺は、一部の人々にとって役立つかもしれません:

2の累乗は、2進数で表現すると、常に1の後にn個のゼロ続くように見えます。nは0以上です。例:

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

等々。

1これらの種類の数から差し引くと、それらは0になり、その後にn個が続き、 nは上記と同じです。例:

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

等々。

核心に来る

x2のべき乗である数値のビットごとのANDを実行するとx - 1どうなりますか?

1 xのゼロに合わせますx - 1と、すべてのゼロはxのものと整合しますx - 1ビット単位の原因、と0になるためにそしてそれは我々が右であること上記の単一ラインの答えを持っているかです。


上記の受け入れられた回答の美しさにさらに追加-

それで、今私たちは自由に使えるプロパティを持っています:

任意の数から1を引くと、バイナリ表現では、右端の1が0になり、その右端の1の前のすべてのゼロが1になります。

このプロパティのすばらしい使い方の1つは、見つけ出すことです。指定された数値のバイナリ表現に1がいくつあるのでしょうか。与えられた整数に対してそれを行うための短くて甘いコードxは:

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

上で説明した概念から証明できる数のもう1つの側面は、「すべての正の数を2の累乗の和として表すことができるか?」

はい、すべての正の数は2の累乗の合計として表すことができます。任意の数については、その2進数表現を使用します。例:テイク番号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:どこかで0が正の数であると主張しましたか?または2の累乗?
displayName

はい、例として0を入れて、そのバイナリ表現の内部でその計算を行います。それは混乱を引き起こします。
ミチ

1
2つの数値を追加することで、それらが正である必要があると信じて混乱させる場合、私はそれについて何もすることができません。さらに、この数では2のべき乗が省略されていることを示すために、表現に0が示されています。基本的な数学を知っている人なら誰でも、0を追加することは何も追加しないことを意味することを知っています。
displayName 2019

10

質問を投稿した後、私は次の解決策を考えました:

2進数の1つが1であるかどうかを確認する必要があります。したがって、数値を一度に1桁ずつ右にシフトし、trueそれが1に等しい場合は戻ります。いずれかの時点で奇数((number & 1) == 1)が見つかった場合、結果はになりfalseます。これは、(ベンチマークを使用して)(大きな)真の値に対して元の方法よりわずかに速く、偽または小さな値に対してはるかに速く証明されました。

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

もちろん、Gregのソリューションははるかに優れています。


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

そして、ある数が別の数の累乗であるかどうかを見つけるための一般的なアルゴリズムを以下に示します。

    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
これc#ですか?これはブール値として返されるのとc++同じxです。
マリアーノデサンツェ

1
私はそれをC ++として書きました。それをC#にするのは簡単です:bool isPow2 =((x&〜(x-1))== x)?x!= 0:false;
abelenky

4

指定された数値が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;
}

または、C#の場合:return x == Math.Pow(2、Math.Log(x、2));
コンフィギュレータ

4
壊れた。主要な浮動小数点丸めの問題に苦しんでいます。浮動小数点を使用したい場合frexpは、厄介なlogものではなく使用してください。
R .. GitHub ICE HELPING 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));
}

これは本当に速いです。すべての2 ^ 32整数をチェックするには、約6分43秒かかります。


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

xが2の累乗である場合、その唯一の1ビットは位置にありnます。これはx – 1、位置が0であることを意味しますn。理由を確認するには、バイナリ減算のしくみを思い出してください。から1を引くxと、借入は位置まで伝搬しnます。ビットnは0になり、すべての下位ビットは1になります。今、xと共通する1ビットがないためx – 1x & (x – 1)は0であり、!(x & (x – 1))真です。


3

設定ビットが1つしかない場合、数値は2の累乗です。このプロパティとジェネリック関数countSetBitsを使用して、数値が2のべき乗かどうかを調べることができます。

これは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;
}

0の場合もFalseを返すため、0が2の累乗であるかどうかを明示的にチェックする必要はありません。

出力

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

関数の戻り値の型が「ulong」の場合にcを「int」として返しますか?使用するwhile代わりにif?個人的には理由はわかりませんが、うまくいくようです。編集:-いいえ...それは何かより大きい場合は1を返し0ます!?
James Khoury、2012年

@JamesKhoury c ++プログラムを作成していたため、誤ってintを返していました。しかし、それは小さなタイプミスであり、反対票に値するものではありませんでした。しかし、「ifの代わりにwhileを使用する」および「0より大きいものに対して1を返す」というコメントの残りの理由を理解できません。出力を確認するためにメインスタブを追加しました。私の知る限り、その期待される出力。私が間違っていたら訂正してください。
jerrymouse 2012年

3

ここに私が考案した別の方法があります。この場合、|代わりにを使用します&

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

(x > 0)ここにビットが必要ですか?
コンフィギュレータ

@configurator、はい、そうでなければis_power_of_2(0)はtrueを返します
Chethan

3

任意の2の累乗に対して、以下も成り立ちます。

n&(-n)== n

注:n = 0の場合は失敗するため、それを確認する必要があります。
これが機能する理由は次のとおりです。
-nはnの2の補数です。-nは、nと比較して、nの右端の設定ビットの左側にあるすべてのビットを反転します。2の累乗の場合、設定ビットは1つだけです。


2

0000 0001    Yes
0001 0001    No

アルゴリズム

  1. ビットマスクを使用してNUM、変数をバイナリで除算する

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

  3. それ以外の場合は、NUMゼロ以外の値になります

  4. IF NUM = 1: Return TRUE

  5. それ以外の場合は、ステップ1に進みます。

複雑

時間〜O(log(d))どこdバイナリ桁数です


1

ビット演算なしで@ user134548の答えを改善する:

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

これは次の場合にうまく機能します:

IsPowerOfTwo(9223372036854775809)

浮動小数点演算は単純なビットごとの式よりはるかに遅い
phuclv

1

.NET Core 3、System.Runtime.Intrinsics.X86.Popcnt.PopCountがある場合、Mark gravellがこれを提案しました

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

単一の命令、より高速です(x != 0) && ((x & (x - 1)) == 0)が移植性が低くなります。


あなたはそれがより速いと確信していますか(x != 0) && ((x & (x - 1)) == 0)?私はそれを疑う、特に。popcntが利用できない古いシステム
phuclv

速くはありません。私は最近のIntel CPUでこれをテストし、逆アセンブリで使用されているPOPCNTを確認しました(.NETではなくCコードで許可されています)。POPCNTは一般にビットをカウントするのに高速ですが、シングルビットオンの場合、ビットトゥウィドルトリックは10%高速です。
エラウル

エラーが発生しました。分岐予測が「だまされている」と思った場合、私はループでテストしていました。POPCNTは確かに単一のクロックサイクルで実行される単一の命令であり、使用可能な場合はより高速です。
eraoul

0

Cでは、i && !(i & (i - 1)トリックをテストし、__builtin_popcount(i)Linuxでgccを使用してと比較し、CPUのPOPCNT命令を確実に使用するために-mpopcntフラグを指定しました。私のテストプログラムでは、2の累乗である0から2 ^ 31までの整数の数を数えました。

i && !(i & (i - 1)が使用した分解でPOPCNTが使用されていることを確認したにもかかわらず、最初は10%速いと思いました__builtin_popcount

しかし、ifステートメントが含まれていることに気付き、分岐予測はビットいじりバージョンでおそらくよりうまく機能していました。ifを削除したところ、POPCNTは予想どおりに速くなりました。

結果:

インテル(R)Core(TM)i7-4771 CPU最大3.90 GHz

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

AMD Ryzen Threadripper 2950X 16コアプロセッサ最大3.50 GHz

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

ここで、Intel CPUはAMDと比べてビットが少し変化しているように少し遅いように見えますが、POPCNTははるかに高速です。AMD POPCNTはそれほど効果がありません。

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

テストを実行します。

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

多くの回答がn &&!(n&(n-1))を返すことを提案しているのを目にしますが、私の経験では、入力値が負の場合、偽の値を返します。ここでは、2の累乗の設定ビットが1つしかないことがわかっているので、ここでは別の簡単なアプローチを説明します。設定ビットの数を数えるだけで、O(log N)時間かかります。

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

いいえこの記事をチェックしてください。セットビットの


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

番号9223372036854775809でそれを試してください。それは動作しますか?丸め誤差のため、私はそうは思いません。
コンフィギュレー

1
@configurator 922337203685477580_9_は、私にとって2の累乗のようには見えません;)
Kirschstein 2010年

1
@キルシュシュタイン:その数は彼に偽陽性を与えました。
Erich Mirabal

7
キルシュシュタイン:それも私には似ていません。それは機能のように見えますが...
configurator

-2

このJavaのプログラムは、数値が2のべき乗である場合は「true」を返し、2のべき乗でない場合は「false」を返します。

// 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#のタグが付けられており、ソリューションも以前のソリューションと比較して非常に遅いです
phuclv
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.