Heute brauchte ich einen einfachen Algorithmus, um zu überprüfen, ob eine Zahl eine Zweierpotenz ist.
Der Algorithmus muss sein:
- Einfach
- Korrigieren Sie für jeden
ulong
Wert.
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 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;
}
Dies ergab true
für den angegebenen falschen Wert : 9223372036854775809
.
Gibt es einen besseren Algorithmus?
(x & (x - 1))
kann falsch positive Ergebnisse zurückgeben, wennX
es sich um eine Summe von Zweierpotenzen handelt, z8 + 16
.Antworten:
Für dieses Problem gibt es einen einfachen Trick:
Beachten Sie , wird diese Funktion berichten
true
für0
, die keine Zweierpotenz ist2
. Wenn Sie dies ausschließen möchten, gehen Sie wie folgt vor:Erläuterung
In erster Linie der bitweise Binär- und Operator aus der MSDN-Definition:
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:
Jetzt ersetzen wir jedes Vorkommen von x durch 4:
Nun, wir wissen bereits, dass 4! = 0 wahr ist, soweit so gut. Aber was ist mit:
Dies bedeutet natürlich:
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:
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. Also1 & 1 = 1
,1 & 0 = 0
,0 & 0 = 0
, und0 & 1 = 0
. Also rechnen wir nach:Das Ergebnis ist einfach 0. Also gehen wir zurück und schauen uns an, was unsere return-Anweisung jetzt übersetzt:
Was jetzt übersetzt bedeutet:
Wir alle wissen, dass dies
true && true
einfach isttrue
, und dies zeigt, dass für unser Beispiel 4 eine Potenz von 2 ist.quelle
Einige Websites, die diesen und andere kleine Hacks dokumentieren und erklären, sind:
( http://graphics.stanford.edu/~seander/bithacks.html#DetermineIfPowerOf2 )
( http://bits.stephan-brumme.com/isPowerOfTwo.html )
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:um dieses Problem zu beheben.
quelle
!
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ürulong
.)return (i & -i) == i
quelle
i
dem 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 voni & -i
ist daher das niedrigstwertige gesetzte Bit ini
(was eine Zweierpotenz ist). Wenni
der gleiche Wert vorliegt, wurde nur dieses Bit gesetzt. Es schlägt fehl, wenni
es aus demselben Grund 0i & (i - 1) == 0
ist.i
es 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.i==0
(gibt zurück,(0&0==0)
was isttrue
). Es sollte seinreturn i && ( (i&-i)==i )
quelle
Ich habe kürzlich einen Artikel darüber unter http://www.exploringbinary.com/ten-ways-to-check-if-an-integer-is-a-power-of-two-in-c/ geschrieben . Es behandelt die Bitzählung, die korrekte Verwendung von Logarithmen, die klassische Prüfung "x &&! (X & (x - 1))" und andere.
quelle
Hier ist eine einfache C ++ - Lösung:
quelle
__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.popcnt
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:
und so weiter.
Wenn wir
1
von dieser Art von Zahlen subtrahieren , werden sie zu 0, gefolgt von n Einsen, und wieder ist n dasselbe wie oben. Ex:und so weiter.
Auf den Punkt kommen
Die Eins von
x
wird mit der Null von ausgerichtetx - 1
und alle Nullen vonx
werden mit Einsen von ausgerichtetx - 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:
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
x
lautet: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
.quelle
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,
true
wenn sie gleich 1 ist. Wenn wir zu irgendeinem Zeitpunkt durch eine ungerade Zahl ((number & 1) == 1
) kommen, wissen wir, dass das Ergebnis istfalse
. 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.Natürlich ist Gregs Lösung viel besser.
quelle
Und hier ist ein allgemeiner Algorithmus, um herauszufinden, ob eine Zahl eine Potenz einer anderen Zahl ist.
quelle
quelle
c#
? Ich denke, das ist so,c++
wiex
es als Bool zurückgegeben wird.Finden Sie heraus, ob die angegebene Zahl eine Potenz von 2 ist.
quelle
frexp
eher als böselog
Dinge, wenn Sie Gleitkomma verwenden möchten.quelle
Das geht wirklich schnell. Es dauert ungefähr 6 Minuten und 43 Sekunden, um alle 2 ^ 32 ganzen Zahlen zu überprüfen.
quelle
Wenn
x
es sich um eine Zweierpotenz handelt, befindet sich das einzige 1-Bit in Positionn
. Dies bedeutet, dassx – 1
eine 0 in Position istn
. Um zu sehen, warum, erinnern Sie sich daran, wie eine binäre Subtraktion funktioniert. Wenn 1 von subtrahiert wirdx
, breitet sich der Kredit bis zur Position ausn
. Bitn
wird 0 und alle unteren Bits werden 1. Dax
nun keine 1 Bits gemeinsam sindx – 1
,x & (x – 1)
ist 0 und!(x & (x – 1))
ist wahr.quelle
Eine Zahl ist eine Potenz von 2, wenn sie nur 1 gesetztes Bit enthält. Wir können diese Eigenschaft und die generische Funktion verwenden
countSetBits
um herauszufinden, ob eine Zahl eine Potenz von 2 ist oder nicht.Dies ist ein C ++ - Programm:
Wir müssen nicht explizit prüfen, ob 0 eine Potenz von 2 ist, da es auch für 0 False zurückgibt.
AUSGABE
quelle
while
anstelle einesif
? Ich persönlich kann keinen Grund erkennen, aber es scheint zu funktionieren. EDIT: - nein ... es wird 1 für etwas größer als0
! Zurückgeben .Hier ist eine andere Methode, die ich entwickelt habe, in diesem Fall
|
anstelle von&
:quelle
(x > 0)
bisschen hier?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.
quelle
Beispiel
Algorithmus
Teilen Sie
NUM
die Variable mithilfe einer Bitmaske binärIF R > 0 AND L > 0: Return FALSE
Andernfalls
NUM
wird diejenige ungleich NullIF NUM = 1: Return TRUE
Fahren Sie andernfalls mit Schritt 1 fort
Komplexität
Zeit ~
O(log(d))
wod
ist die Anzahl der Binärziffernquelle
Verbesserung der Antwort von @ user134548 ohne Bitarithmetik:
Dies funktioniert gut für:
quelle
Mark Gravell schlug dies vor, wenn Sie über .NET Core 3, System.Runtime.Intrinsics.X86.Popcnt.PopCount verfügen
Einzelanweisung, schneller als
(x != 0) && ((x & (x - 1)) == 0)
aber weniger portabel.quelle
(x != 0) && ((x & (x - 1)) == 0)
? Ich bezweifle das, insb. auf älteren Systemen, auf denen popcnt nicht verfügbar istIn 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
AMD Ryzen Threadripper 2950X 16-Kern-Prozessor max. 3,50 GHz
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:
Führen Sie Tests durch:
quelle
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.
Überprüfen Sie diesen Artikel, um Nr. Zu zählen. von gesetzten Bits
quelle
quelle
Dieses Programm in Java gibt "true" zurück, wenn number eine Potenz von 2 ist, und "false", wenn es keine Potenz von 2 ist
quelle