Größte Zweierpotenz, die mit Java unter der angegebenen Zahl liegt

1. Übersicht

In diesem Artikel erfahren Sie, wie Sie die größte Potenz von 2 finden, die unter der angegebenen Zahl liegt.

Für unsere Beispiele nehmen wir die Beispieleingabe 9. 20 ist 1, die am wenigsten gültige Eingabe, für die wir die Potenz von 2 kleiner als die angegebene Eingabe finden können, ist 2. Daher betrachten wir nur Eingaben größer als 1 als gültig .

2. Naiver Ansatz

Beginnen wir mit 20, was 1 ist, und multiplizieren die Zahl so lange mit 2, bis wir eine Zahl finden, die kleiner als die Eingabe ist :

public long findLargestPowerOf2LessThanTheGivenNumber(long input) { Assert.isTrue(input > 1, "Invalid input"); long firstPowerOf2 = 1; long nextPowerOf2 = 2; while (nextPowerOf2 < input) { firstPowerOf2 = nextPowerOf2; nextPowerOf2 = nextPowerOf2 * 2; } return firstPowerOf2; }

Lassen Sie uns zu verstehen , den Code für die Probeneingabe = 9.

Der Anfangswert für firstPowerOf2 ist 1 und nextPowerOf2 ist 2. Wie wir sehen können, ist 2 <9 wahr und wir gelangen in die while-Schleife.

Für die erste Iteration ist firstPowerOf2 2 und nextPowerOf2 2 * 2 = 4. Wieder 4 <9, also lassen Sie uns die while-Schleife fortsetzen.

Für die zweite Iteration ist firstPowerOf2 4 und nextPowerOf2 4 * 2 = 8. Jetzt 8 <9, machen wir weiter.

Für die dritte Iteration ist firstPowerOf2 8 und nextPowerOf2 8 * 2 = 16. Die while-Bedingung 16 <9 ist falsch und bricht daher aus der while-Schleife aus. 8 ist die größte Potenz von 2, die kleiner als 9 ist.

Lassen Sie uns einige Tests durchführen, um unseren Code zu validieren:

assertEquals(8, findPowerOf2LessThanTheGivenNumber(9)); assertEquals(16, findPowerOf2LessThanTheGivenNumber(32)); 

Die zeitliche Komplexität unserer Lösung beträgt O (log 2 (N)) . In unserem Fall haben wir log 2 (9) = 3 Mal wiederholt .

3. Verwenden von Math.log

Die logarithmische Basis 2 gibt an, wie oft wir eine Zahl rekursiv durch 2 teilen können, mit anderen Worten, log 2 einer Zahl ergibt die Potenz von 2 . Schauen wir uns einige Beispiele an, um dies zu verstehen.

log 2 (8) = 3 und log 2 (16) = 4. Im Allgemeinen können wir sehen, dass y = log 2 x ist, wobei x = 2y.

Wenn wir also eine Zahl finden, die durch 2 teilbar ist, subtrahieren wir 1 davon, um ein Szenario zu vermeiden, in dem die Zahl eine perfekte Potenz von 2 ist.

Math.log ist Protokoll 10 . Um log 2 (x) zu berechnen , können wir die Formel log 2 (x) = log 10 (x) / log 10 (2) verwenden.

Lassen Sie uns das in Code einfügen:

public long findLargestPowerOf2LessThanTheGivenNumberUsingLogBase2(long input) { Assert.isTrue(input > 1, "Invalid input"); long temp = input; if (input % 2 == 0) { temp = input - 1; } long power = (long) (Math.log(temp) / Math.log(2)); long result = (long) Math.pow(2, power); return result; }

Unter der Annahme, dass unsere Beispieleingabe 9 ist, beträgt der Anfangswert von temp 9.

9% 2 ist 1, also ist unsere Temperaturvariable 9.Hier verwenden wir die Modulo-Division, die den Rest von 9/2 ergibt.

Um das Protokoll 2 (9) zu finden, führen wir Protokoll 10 (9) / Protokoll 10 (2) = 0,95424 / 0,30103 ~ = 3 aus.

Das Ergebnis ist 23, was 8 ist.

Lassen Sie uns unseren Code überprüfen:

assertEquals(8, findLargestPowerOf2LessThanTheGivenNumberUsingLogBase2(9)); assertEquals(16, findLargestPowerOf2LessThanTheGivenNumberUsingLogBase2(32));

In Wirklichkeit wird Math.pow dieselbe Iteration durchführen wie in Ansatz 1. Daher können wir sagen, dass auch für diese Lösung die zeitliche Komplexität O ist (Log 2 (N)) .

4. Bitweise Technik

Für diesen Ansatz verwenden wir die bitweise Verschiebungstechnik. Schauen wir uns zunächst die binären Darstellungen für die Potenz von 2 an, wobei wir 4 Bits zur Darstellung der Zahl haben

20 1 0001
21 2 0010
22 4 0100
23 8 1000

Bei genauerem Hinsehen können wir beobachten, dass wir die Potenz von 2 berechnen können, indem wir die Bytes für 1 nach links verschieben . dh. 22 bleibt Verschiebungsbytes für 1 mal 2 Stellen und so weiter.

Lassen Sie uns mit der Bitshift-Technik codieren:

public long findLargestPowerOf2LessThanTheGivenNumberUsingBitShiftApproach(long input) { Assert.isTrue(input > 1, "Invalid input"); long result = 1; long powerOf2; for (long i = 0; i < Long.BYTES * 8; i++) { powerOf2 = 1 <= input) { break; } result = powerOf2; } return result; }

Im obigen Code verwenden wir long als unseren Datentyp, der 8 Bytes oder 64 Bit verwendet. Wir berechnen also die Potenz von 2 bis 264. Wir verwenden den Bitverschiebungsoperator << , um die Potenz von 2 zu ermitteln. Für unseren Beispieleingang 9 ist nach der 4. Iteration der Wert von powerOf2 = 16 und result = 8 wo wir aus der Schleife ausbrechen als 16> 9 die Eingabe .

Lassen Sie uns überprüfen, ob unser Code wie erwartet funktioniert:

assertEquals(8, findLargestPowerOf2LessThanTheGivenNumberUsingBitShiftApproach(9)); assertEquals(16, findLargestPowerOf2LessThanTheGivenNumberUsingBitShiftApproach(32));

Die Zeitkomplexität im ungünstigsten Fall für diesen Ansatz ist wieder O (log 2 (N)) , ähnlich wie wir es für den ersten Ansatz gesehen haben. Dieser Ansatz ist jedoch besser, da eine Bitverschiebungsoperation im Vergleich zur Multiplikation effizienter ist .

5. Bitweises UND

Für unseren nächsten Ansatz verwenden wir diese Formel 2n UND 2n -1 = 0 .

Schauen wir uns einige Beispiele an, um zu verstehen, wie es funktioniert.

Die binäre Darstellung von 4 ist 0100 und 3 ist 0011 .

Lassen Sie uns diese beiden Zahlen bitweise UND-verknüpfen. 0100 UND 0011 ist 0000 . Wir können dasselbe für jede Potenz von 2 und eine Zahl weniger sagen. Nehmen wir 16 (24) , und 15 , die , wie dargestellt ist , 1000 , 0111 verbunden. Wieder sehen wir, dass das bitweise UND für diese beiden zu 0 führt. Wir können auch sagen, dass die UND-Verknüpfung für eine andere Zahl als diese 2 nicht zu einer 0 führt.

Sehen wir uns den Code zur Lösung dieses Problems mit bitweisem UND an:

public long findLargestPowerOf2LessThanTheGivenNumberUsingBitwiseAnd(long input) { Assert.isTrue(input > 1, "Invalid input"); long result = 1; for (long i = input - 1; i > 1; i--) { if ((i & (i - 1)) == 0) { result = i; break; } } return result; }

Im obigen Code durchlaufen wir Zahlen, die kleiner sind als unsere Eingabe. Immer wenn wir das bitweise UND einer Zahl finden und die Zahl 1 Null ist, brechen wir aus der Schleife aus, da wir wissen, dass die Zahl eine Potenz von 2 ist. In diesem Fall brechen wir für unseren Sample- Eingang 9 aus der Schleife aus wenn i = 8 und i - 1 = 7.

Lassen Sie uns nun einige Szenarien überprüfen:

assertEquals(8, findLargestPowerOf2LessThanTheGivenNumberUsingBitwiseAnd(9)); assertEquals(16, findLargestPowerOf2LessThanTheGivenNumberUsingBitwiseAnd(32));

Die Zeitkomplexität im ungünstigsten Fall für diesen Ansatz ist O (N / 2), wenn der Eingang eine exakte Potenz 2 ist. Wie wir sehen können, ist dies nicht die effizienteste Lösung, aber es ist gut, diese Technik so zu kennen, wie sie kommen könnte praktisch bei der Lösung ähnlicher Probleme.

6. Fazit

Wir haben verschiedene Ansätze gesehen, um die größte Potenz von 2 zu finden, die kleiner als die angegebene Zahl ist. Wir haben auch festgestellt, wie bitweise Operationen in einigen Fällen die Berechnungen vereinfachen können.

Den vollständigen Quellcode mit Unit-Tests für diesen Artikel finden Sie auf GitHub.