So überprüfen Sie, ob eine Zahl eine Potenz von 2 ist

584

Heute brauchte ich einen einfachen Algorithmus, um zu überprüfen, ob eine Zahl eine Zweierpotenz ist.

Der Algorithmus muss sein:

  1. Einfach
  2. Korrigieren Sie für jeden ulongWert.

Ich habe mir diesen einfachen Algorithmus ausgedacht:

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

Aber dann dachte ich, wie wäre es zu überprüfen, ob es sich um eine genau runde Zahl handelt? Aber als ich nach 2 ^ 63 + 1 gesucht habe, habe ich wegen Rundung genau 63 zurückgegeben. Also habe ich geprüft, ob 2 hoch 63 gleich der ursprünglichen Zahl ist - und das liegt daran, dass die Berechnung in s und nicht in exakten Zahlen erfolgt: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;
}

Dies ergab truefür den angegebenen falschen Wert : 9223372036854775809.

Gibt es einen besseren Algorithmus?

Konfigurator
quelle
1
Ich denke, die Lösung (x & (x - 1))kann falsch positive Ergebnisse zurückgeben, wenn Xes sich um eine Summe von Zweierpotenzen handelt, z 8 + 16.
Joe Brown
32
Alle Zahlen können als Summe von Zweierpotenzen geschrieben werden. Deshalb können wir jede Zahl binär darstellen. Darüber hinaus gibt Ihr Beispiel kein falsches Positiv zurück, da 11000 & 10111 = 10000! = 0.
vlsd
1
@ JoeBrown Es hat keine Fehlalarme. Tatsächlich gibt der Ausdruck die größere einer beliebigen Summe von zwei Zweierpotenzen zurück.
Samy Bencherif

Antworten:

1219

Für dieses Problem gibt es einen einfachen Trick:

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

Beachten Sie , wird diese Funktion berichten truefür 0, die keine Zweierpotenz ist 2. Wenn Sie dies ausschließen möchten, gehen Sie wie folgt vor:

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

Erläuterung

In erster Linie der bitweise Binär- und Operator aus der MSDN-Definition:

Binär & Operatoren sind für die Integraltypen und Bool vordefiniert. & Berechnet für integrale Typen das logische bitweise UND seiner Operanden. Berechnet für Bool-Operanden das logische UND seiner Operanden; Das heißt, das Ergebnis ist genau dann wahr, wenn beide Operanden wahr sind.

Schauen wir uns nun an, wie sich das alles entwickelt:

Die Funktion gibt einen Booleschen Wert (true / false) zurück und akzeptiert einen eingehenden Parameter vom Typ unsigned long (in diesem Fall x). Nehmen wir der Einfachheit halber an, dass jemand den Wert 4 übergeben und die Funktion folgendermaßen aufgerufen hat:

bool b = IsPowerOfTwo(4)

Jetzt ersetzen wir jedes Vorkommen von x durch 4:

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

Nun, wir wissen bereits, dass 4! = 0 wahr ist, soweit so gut. Aber was ist mit:

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

Dies bedeutet natürlich:

((4 & 3) == 0)

Aber was genau ist das 4&3?

Die binäre Darstellung von 4 ist 100 und die binäre Darstellung von 3 ist 011 (denken Sie daran, dass & die binäre Darstellung dieser Zahlen übernimmt). Also haben wir:

100 = 4
011 = 3

Stellen Sie sich vor, diese Werte stapeln sich ähnlich wie die elementare Addition. Der &Betreiber sagt , dass , wenn beide Werte gleich 1 sind dann das Ergebnis 1 ist, sonst 0. Also 1 & 1 = 1, 1 & 0 = 0, 0 & 0 = 0, und 0 & 1 = 0. Also rechnen wir nach:

100
011
----
000

Das Ergebnis ist einfach 0. Also gehen wir zurück und schauen uns an, was unsere return-Anweisung jetzt übersetzt:

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

Was jetzt übersetzt bedeutet:

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

Wir alle wissen, dass dies true && trueeinfach ist true, und dies zeigt, dass für unser Beispiel 4 eine Potenz von 2 ist.

Greg Hewgill
quelle
56
@Kripp: Die Zahl hat die binäre Form 1000 ... 000. Wenn Sie es -1, hat es die Form 0111 ... 111. Somit ist die Binärzahl der beiden Zahlen 000000. Dies würde bei Nicht-Zweierpotenzen nicht passieren, da 1010100 beispielsweise zu 1010011 werden würde, was zu einem (Fortsetzung ...)
Konfigurator am
47
... ergibt einen 1010000 nach dem binären und. Das einzige falsch positive wäre 0, weshalb ich verwenden würde: return (x! = 0) && ((x & (x - 1)) == 0);
Konfigurator
6
Kripp, betrachte (2: 1, 10: 1) (4: 3, 100: 11) (8: 7, 1000: 111) (16:15, 10000: 1111) Siehst du das Muster?
Thomas L Holaday
13
@ShuggyCoUk: Zweierkomplement ist, wie negative Zahlen dargestellt werden. Da dies eine vorzeichenlose Ganzzahl ist, ist die Darstellung negativer Zahlen nicht relevant. Diese Technik beruht nur auf der binären Darstellung nichtnegativer Ganzzahlen.
Greg Hewgill
4
@ SoapBox - was ist häufiger? Nullen oder Zahlen ungleich Null, die keine Zweierpotenzen sind? Dies ist eine Frage, die Sie ohne weiteren Kontext nicht beantworten können. Und es ist wirklich, wirklich egal.
Konfigurator
97

Einige Websites, die diesen und andere kleine Hacks dokumentieren und erklären, sind:

Und der Großvater von ihnen, das Buch "Hacker's Delight" von Henry Warren Jr . :

Wie Sean Andersons Seite erklärt, zeigt der Ausdruck ((x & (x - 1)) == 0)fälschlicherweise an, dass 0 eine Potenz von 2 ist. Er schlägt vor, Folgendes zu verwenden:

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

um dieses Problem zu beheben.

Michael Burr
quelle
4
0 ist eine Potenz von 2 ... 2 ^ -inf = 0 .;););)
Michael Bray
4
Da es sich um einen mit C # gekennzeichneten Thread handelt, sollte darauf hingewiesen werden, dass der letzte Ausdruck (von Sean Anderson) in C # unzulässig ist, da er !nur auf boolesche Typen angewendet werden kann und &&außerdem erfordert, dass beide Operanden boolesch sind (mit Ausnahme der benutzerdefinierten Operatoren) andere Dinge möglich machen, aber das ist nicht relevant für ulong.)
Jeppe Stig Nielsen
40

return (i & -i) == i

Andreas Petersson
quelle
2
Gibt es einen Hinweis, warum dies funktioniert oder nicht? Ich habe die Richtigkeit nur in Java überprüft, wo es nur signierte Ints / Longs gibt. Wenn es richtig ist, wäre dies die überlegene Antwort. schneller + kleiner
Andreas Petersson
7
Es nutzt eine der Eigenschaften der Zweierkomplementnotation: Um den negativen Wert einer Zahl zu berechnen, führen Sie eine bitweise Negation durch und addieren 1 zum Ergebnis. Das niedrigstwertige Bit, von idem gesetzt ist, wird ebenfalls gesetzt -i. Die darunter liegenden Bits sind 0 (in beiden Werten), während die darüber liegenden Bits in Bezug zueinander invertiert werden. Der Wert von i & -iist daher das niedrigstwertige gesetzte Bit in i(was eine Zweierpotenz ist). Wenn ider gleiche Wert vorliegt, wurde nur dieses Bit gesetzt. Es schlägt fehl, wenn ies aus demselben Grund 0 i & (i - 1) == 0ist.
Michael Carman
6
Wenn ies sich um einen vorzeichenlosen Typ handelt, hat das Zweierkomplement nichts damit zu tun. Sie nutzen lediglich die Eigenschaften der modularen Arithmetik und der bitweisen und.
R .. GitHub STOP HELPING ICE
2
Dies funktioniert nicht, wenn i==0(gibt zurück, (0&0==0)was ist true). Es sollte seinreturn i && ( (i&-i)==i )
Bobobobo
22
bool IsPowerOfTwo(ulong x)
{
    return x > 0 && (x & (x - 1)) == 0;
}
Matt Howells
quelle
3
Diese Lösung ist besser, weil sie auch mit negativen Zahlen umgehen kann, wenn negative Zahlen passieren konnten (wenn lang statt ulong)
Steven
Warum wird in diesem Fall eine Dezimalstelle als Zweierpotenz übergeben?
Chris Frisina
17

Hier ist eine einfache C ++ - Lösung:

bool IsPowerOfTwo( unsigned int i )
{
    return std::bitset<32>(i).count() == 1;
}
deft_code
quelle
8
auf gcc wird dies zu einem einzigen eingebauten gcc kompiliert, der aufgerufen wird __builtin_popcount. Leider verfügt eine Prozessorfamilie noch nicht über eine einzige Assembly-Anweisung (x86). Stattdessen ist dies die schnellste Methode zum Bitzählen. Bei jeder anderen Architektur ist dies eine einzelne Montageanweisung.
Deft_code
3
@deft_code Unterstützung für neuere x86-Mikroarchitekturenpopcnt
phuclv
13

Der folgende Nachtrag zur akzeptierten Antwort kann für einige Personen nützlich sein:

Eine Zweierpotenz, wenn sie binär ausgedrückt wird, sieht immer wie 1 aus, gefolgt von n Nullen, wobei n größer oder gleich 0 ist. Beispiel:

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

und so weiter.

Wenn wir 1von dieser Art von Zahlen subtrahieren , werden sie zu 0, gefolgt von n Einsen, und wieder ist n dasselbe wie oben. Ex:

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

und so weiter.

Auf den Punkt kommen

Was passiert, wenn wir ein bitweises UND einer Zahl machen x, die eine Potenz von 2 ist, und x - 1?

Die Eins von xwird mit der Null von ausgerichtet x - 1und alle Nullen von xwerden mit Einsen von ausgerichtet x - 1, wodurch das bitweise UND zu 0 führt. Und so haben wir die oben erwähnte einzeilige Antwort richtig.


Weitere Ergänzung zur Schönheit der oben akzeptierten Antwort -

Wir haben jetzt also eine Immobilie zur Verfügung:

Wenn wir 1 von einer beliebigen Zahl subtrahieren, wird in der Binärdarstellung die am weitesten rechts stehende 1 zu 0 und alle Nullen vor dieser am weitesten rechts stehenden 1 werden jetzt zu 1

Eine großartige Verwendung dieser Eigenschaft besteht darin, herauszufinden, wie viele Einsen in der binären Darstellung einer bestimmten Zahl vorhanden sind. Der kurze und süße Code dafür für eine bestimmte Ganzzahl xlautet:

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

Ein weiterer Aspekt von Zahlen, der aus dem oben erläuterten Konzept bewiesen werden kann, ist "Kann jede positive Zahl als Summe der Potenzen von 2 dargestellt werden?" .

Ja, jede positive Zahl kann als Summe der Potenzen von 2 dargestellt werden. Nehmen Sie für jede Zahl ihre binäre Darstellung. Bsp.: Nummer nehmen 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
Anzeigename
quelle
@Michi: Habe ich irgendwo behauptet, dass 0 eine positive Zahl ist? Oder eine Potenz von 2?
DisplayName
Ja, indem Sie 0 als Beispiel setzen und diese Mathematik in dieser binären Darstellung darauf schreiben. Es schafft eine Verwirrung.
Michi
1
Wenn Sie durch das Hinzufügen von zwei Zahlen glauben, dass sie positiv sein müssen, kann ich nichts dagegen tun. Ferner wurde in der Darstellung gezeigt, dass Nullen bedeuten, dass diese Potenz von 2 in dieser Zahl übersprungen wird. Jeder, der sich mit Mathematik auskennt, weiß, dass das Hinzufügen von 0 bedeutet, nichts hinzuzufügen.
DisplayName
10

Nachdem ich die Frage gestellt hatte, dachte ich an die folgende Lösung:

Wir müssen prüfen, ob genau eine der Binärziffern eine ist. Also verschieben wir die Zahl einfach um jeweils eine Ziffer nach rechts und kehren zurück, truewenn sie gleich 1 ist. Wenn wir zu irgendeinem Zeitpunkt durch eine ungerade Zahl ( (number & 1) == 1) kommen, wissen wir, dass das Ergebnis ist false. Dies erwies sich (unter Verwendung eines Benchmarks) als etwas schneller als die ursprüngliche Methode für (große) wahre Werte und viel schneller für falsche oder kleine Werte.

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

Natürlich ist Gregs Lösung viel besser.

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

Und hier ist ein allgemeiner Algorithmus, um herauszufinden, ob eine Zahl eine Potenz einer anderen Zahl ist.

    bool IsPowerOf(int n,int b)
    {
        if (n > 1)
        {
            while (n % b == 0)
            {
                n /= b;
            }
        }
        return n == 1;
    }
Raz Megrelidze
quelle
6
bool isPow2 = ((x & ~(x-1))==x)? !!x : 0;
abelenky
quelle
1
Ist das c#? Ich denke, das ist so, c++wie xes als Bool zurückgegeben wird.
Mariano Desanze
1
Ich habe es als C ++ geschrieben. Um es zu machen, ist C # trivial: bool isPow2 = ((x & ~ (x-1)) == x)? x! = 0: false;
Abelenky
4

Finden Sie heraus, ob die angegebene Zahl eine Potenz von 2 ist.

#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;
}
udhaya
quelle
Oder in C #: return x == Math.Pow (2, Math.Log (x, 2));
Konfigurator
4
Gebrochen. Leidet unter großen Gleitkomma-Rundungsproblemen. Verwenden Sie frexpeher als böse logDinge, wenn Sie Gleitkomma verwenden möchten.
R .. GitHub STOP 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;
}
Käfer König
quelle
4
int isPowerOfTwo(unsigned int x)
{
    return ((x != 0) && ((x & (~x + 1)) == x));
}

Das geht wirklich schnell. Es dauert ungefähr 6 Minuten und 43 Sekunden, um alle 2 ^ 32 ganzen Zahlen zu überprüfen.

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

Wenn xes sich um eine Zweierpotenz handelt, befindet sich das einzige 1-Bit in Position n. Dies bedeutet, dass x – 1eine 0 in Position ist n. Um zu sehen, warum, erinnern Sie sich daran, wie eine binäre Subtraktion funktioniert. Wenn 1 von subtrahiert wird x, breitet sich der Kredit bis zur Position aus n. Bit nwird 0 und alle unteren Bits werden 1. Da xnun keine 1 Bits gemeinsam sind x – 1, x & (x – 1)ist 0 und !(x & (x – 1))ist wahr.

Prakash Jat
quelle
3

Eine Zahl ist eine Potenz von 2, wenn sie nur 1 gesetztes Bit enthält. Wir können diese Eigenschaft und die generische Funktion verwendencountSetBits um herauszufinden, ob eine Zahl eine Potenz von 2 ist oder nicht.

Dies ist ein C ++ - Programm:

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

Wir müssen nicht explizit prüfen, ob 0 eine Potenz von 2 ist, da es auch für 0 False zurückgibt.

AUSGABE

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
Jerrymouse
quelle
c als 'int' zurückgeben, wenn die Funktion den Rückgabetyp 'ulong' hat? Verwenden Sie ein whileanstelle eines if? Ich persönlich kann keinen Grund erkennen, aber es scheint zu funktionieren. EDIT: - nein ... es wird 1 für etwas größer als 0! Zurückgeben .
James Khoury
@JamesKhoury Ich habe ein C ++ - Programm geschrieben, also habe ich fälschlicherweise ein int zurückgegeben. Dies war jedoch ein kleiner Tippfehler und verdiente keine Ablehnung. Aber ich verstehe die Gründe für den Rest Ihres Kommentars "Verwenden von while anstelle von if" und "es wird 1 für etwas größer als 0 zurückgeben" nicht. Ich habe den Hauptstub hinzugefügt, um die Ausgabe zu überprüfen. AFAIK ist die erwartete Leistung. Korrigieren Sie mich, wenn ich falsch liege.
Jerrymouse
3

Hier ist eine andere Methode, die ich entwickelt habe, in diesem Fall |anstelle von &:

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));
}
Chethan
quelle
Benötigen Sie das (x > 0)bisschen hier?
Konfigurator
@configurator, ja, sonst würde is_power_of_2 (0) true zurückgeben
Chethan
3

Für jede Potenz von 2 gilt auch das Folgende.

n & (- n) == n

HINWEIS: schlägt für n = 0 fehl, muss also überprüft werden.
Grund dafür ist:
-n ist das 2s-Komplement von n. -n wird jedes Bit links vom am weitesten rechts gesetzten Bit von n im Vergleich zu n umgedreht haben. Für Potenzen von 2 gibt es nur ein gesetztes Bit.

FReeze FRancis
quelle
2

Beispiel

0000 0001    Yes
0001 0001    No

Algorithmus

  1. Teilen Sie NUMdie Variable mithilfe einer Bitmaske binär

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

  3. Andernfalls NUMwird diejenige ungleich Null

  4. IF NUM = 1: Return TRUE

  5. Fahren Sie andernfalls mit Schritt 1 fort

Komplexität

Zeit ~ O(log(d))wo dist die Anzahl der Binärziffern

Khaled.K
quelle
1

Verbesserung der Antwort von @ user134548 ohne Bitarithmetik:

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

Dies funktioniert gut für:

IsPowerOfTwo(9223372036854775809)
Rhodan
quelle
Gleitkommaoperationen sind weitaus langsamer als ein einfacher bitweiser Ausdruck
phuclv
1

Mark Gravell schlug dies vor, wenn Sie über .NET Core 3, System.Runtime.Intrinsics.X86.Popcnt.PopCount verfügen

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

Einzelanweisung, schneller als (x != 0) && ((x & (x - 1)) == 0)aber weniger portabel.

jbat100
quelle
Bist du sicher, dass es schneller ist als (x != 0) && ((x & (x - 1)) == 0)? Ich bezweifle das, insb. auf älteren Systemen, auf denen popcnt nicht verfügbar ist
phuclv
Es ist nicht schneller. Ich habe dies gerade auf einer modernen Intel-CPU getestet und POPCNT überprüft, das bei der Demontage verwendet wird (gewährt, in C-Code, nicht in .NET). POPCNT ist im Allgemeinen schneller zum Zählen von Bits, aber für den Einzelbit-Ein-Fall ist der Bit-Twiddling-Trick immer noch um 10% schneller.
Ära
Ups, ich nehme es zurück. Ich habe in einer Schleife getestet, wo ich denke, dass die Vorhersage von Zweigen "Betrug" war. POPCNT ist in der Tat eine einzelne Anweisung, die in einem einzelnen Taktzyklus ausgeführt wird und schneller ist, wenn Sie sie zur Verfügung haben.
Ära
0

In C habe ich den i && !(i & (i - 1)Trick getestet und mit verglichen__builtin_popcount(i) Verwendung von gcc unter Linux mit dem Flag -mpopcnt verglichen, um sicherzugehen, dass der POPCNT-Befehl der CPU verwendet wird. Mein Testprogramm zählte die Anzahl der ganzen Zahlen zwischen 0 und 2 ^ 31, die eine Zweierpotenz waren.

Zuerst dachte ich, das wäre i && !(i & (i - 1)10% schneller, obwohl ich überprüft habe, dass POPCNT bei der Demontage verwendet wurde, bei der ich es verwendet habe__builtin_popcount .

Ich stellte jedoch fest, dass ich eine if-Anweisung eingefügt hatte und die Verzweigungsvorhersage bei der Bit-Twiddling-Version wahrscheinlich besser lief. Ich habe das if entfernt und POPCNT ist erwartungsgemäß schneller gelandet.

Ergebnisse:

Intel (R) Core (TM) i7-4771 CPU max. 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-Kern-Prozessor max. 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

Beachten Sie, dass hier die Intel-CPU mit dem Bit Twiddling etwas langsamer als AMD zu sein scheint, aber einen viel schnelleren POPCNT hat. Der AMD POPCNT bietet nicht so viel Auftrieb.

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

Führen Sie Tests durch:

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
eraoul
quelle
0

Ich sehe, dass viele Antworten vorschlagen, n &&! (N & (n - 1)) zurückzugeben, aber meiner Erfahrung nach gibt es falsche Werte zurück, wenn die Eingabewerte negativ sind. Ich werde hier einen anderen einfachen Ansatz teilen, da wir wissen, dass eine Zweierpotenz nur ein gesetztes Bit hat. Wir werden also einfach die Anzahl der gesetzten Bits zählen, was O (log N) Zeit in Anspruch nimmt.

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

Überprüfen Sie diesen Artikel, um Nr. Zu zählen. von gesetzten Bits

Anil Gupta
quelle
-1
private static bool IsPowerOfTwo(ulong x)
{
    var l = Math.Log(x, 2);
    return (l == Math.Floor(l));
}
user134548
quelle
Versuchen Sie das für die Nummer 9223372036854775809. Funktioniert es? Ich würde nicht denken, wegen Rundungsfehlern.
Konfigurator
1
@configurator 922337203685477580_9_ sieht für mich nicht nach einer Potenz von 2 aus;)
Kirschstein
1
@Kirschstein: Diese Zahl gab ihm ein falsches Positiv.
Erich Mirabal
7
Kirschstein: Für mich sieht es auch nicht so aus. Für die Funktion sieht es allerdings so aus ...
Konfigurator
-2

Dieses Programm in Java gibt "true" zurück, wenn number eine Potenz von 2 ist, und "false", wenn es keine Potenz von 2 ist

// 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
K_holla
quelle
1
Diese Frage ist mit C # gekennzeichnet, und Ihre Lösung ist im Vergleich zu früheren Lösungen ebenfalls sehr langsam [
phuclv