Was ist der effizienteste Weg, um eine Ganzzahl auf die Potenz einer anderen Ganzzahl in C zu erhöhen?
// 2^3
pow(2,3) == 8
// 5^5
pow(5,5) == 3125
c
algorithm
math
exponentiation
Doug T.
quelle
quelle
int
s halten (und nicht an eine riesige Int-Klasse), werden viele Aufrufe von ipow überlaufen. Ich frage mich, ob es eine clevere Möglichkeit gibt, eine Tabelle vorab zu berechnen und alle nicht überfüllten Kombinationen auf eine einfache Tabellensuche zu reduzieren. Dies würde mehr Speicher benötigen als die meisten allgemeinen Antworten, wäre aber möglicherweise effizienter in Bezug auf die Geschwindigkeit.pow()
keine sichere FunktionAntworten:
Potenzierung durch Quadrieren.
Dies ist die Standardmethode für die modulare Exponentiation für große Zahlen in der asymmetrischen Kryptographie.
quelle
while (exp)
undif (exp & 1)
mitwhile (exp != 0)
undif ((exp & 1) != 0)
jeweils.unsigned exp
oder negativexp
richtig behandeln.n*n*n*n*n*n*n*n
7 Multiplikationen. Dieser Algorithmus berechnet stattm=n*n
, danno=m*m
, dannp=o*o
, inp
= n ^ 8, mit nur drei Multiplikationen. Bei großen Exponenten ist der Leistungsunterschied erheblich.Beachten Sie, dass die Potenzierung durch Quadrieren nicht die optimalste Methode ist. Es ist wahrscheinlich das Beste, was Sie als allgemeine Methode tun können, die für alle Exponentenwerte funktioniert, aber für einen bestimmten Exponentenwert gibt es möglicherweise eine bessere Sequenz, die weniger Multiplikationen benötigt.
Wenn Sie beispielsweise x ^ 15 berechnen möchten, erhalten Sie mit der Exponentiationsmethode durch Quadrieren:
Dies sind insgesamt 6 Multiplikationen.
Es stellt sich heraus, dass dies mit "nur" 5 Multiplikationen durch Potenzierung der Additionskette erfolgen kann .
Es gibt keine effizienten Algorithmen, um diese optimale Folge von Multiplikationen zu finden. Aus Wikipedia :
quelle
Wenn Sie 2 auf eine Potenz erhöhen müssen. Der schnellste Weg, dies zu tun, ist eine Bitverschiebung durch die Leistung.
quelle
2 ** 0 == 1 << 0 == 1
Hier ist die Methode in Java
quelle
quelle
pow(1, -1)
verlässt aber trotz eines negativen Exponenten nicht den Bereich von int. Jetzt, wo man zufällig arbeitet, genauso wiepow(-1, -1)
.Wenn Sie den Wert einer Ganzzahl für 2 auf die Potenz von etwas erhöhen möchten, ist es immer besser, die Verschiebungsoption zu verwenden:
pow(2,5)
kann ersetzt werden durch1<<5
Das ist viel effizienter.
quelle
power()
Funktion, die nur für Ganzzahlen funktioniertKomplexität = O (log (exp))
power()
Funktion, um für negative exp und float base zu arbeiten .Komplexität = O (log (exp))
quelle
float
im zweiten vorgestellten Codeblock (zeigen Sie, wiepower(2.0, -3)
berechnet wird).negative exp and float base
Lösung erklären ? Warum verwenden wir temp, trennen exp durch 2 und überprüfen exp (gerade / ungerade)? Vielen Dank!Ein äußerst spezialisierter Fall ist, wenn Sie sagen müssen 2 ^ (- x zum y), wobei x natürlich negativ ist und y zu groß ist, um auf einem int zu verschieben. Sie können immer noch 2 ^ x in konstanter Zeit ausführen, indem Sie mit einem Schwimmer schrauben.
Sie können mehr Potenzen von 2 erhalten, indem Sie ein Double als Basistyp verwenden. (Vielen Dank an die Kommentatoren, die geholfen haben, diesen Beitrag zu korrigieren).
Es besteht auch die Möglichkeit, dass Sie mehr über IEEE-Floats erfahren und andere Sonderfälle der Potenzierung auftreten.
quelle
Nur als Folge von Kommentaren zur Effizienz der Potenzierung durch Quadrieren.
Der Vorteil dieses Ansatzes besteht darin, dass er in log (n) -Zeit ausgeführt wird. Wenn Sie beispielsweise etwas Großes wie x ^ 1048575 (2 ^ 20 - 1) berechnen möchten, müssen Sie die Schleife nur 20 Mal durchlaufen, nicht 1 Million + mit dem naiven Ansatz.
In Bezug auf die Codekomplexität ist es auch einfacher als zu versuchen, die optimalste Folge von Multiplikationen zu finden, wie es a la Pramod vorschlägt.
Bearbeiten:
Ich denke, ich sollte klären, bevor mich jemand auf das Potenzial für einen Überlauf hinweist. Bei diesem Ansatz wird davon ausgegangen, dass Sie über eine riesige Bibliothek verfügen.
quelle
Spät zur Party:
Im Folgenden finden Sie eine Lösung, die auch
y < 0
so gut wie möglich funktioniert.intmax_t
für maximale Reichweite verwendet. Antworten, die nicht passen, sind nicht vorgesehenintmax_t
.powjii(0, 0) --> 1
Das ist ein häufiges Ergebnis für diesen Fall.pow(0,negative)
, ein weiteres undefiniertes Ergebnis, wird zurückgegebenINTMAX_MAX
Dieser Code verwendet eine Forever-Schleife
for(;;)
, um das endgültigebase *= base
Common in anderen Loop-Lösungen zu vermeiden . Diese Multiplikation ist 1) nicht erforderlich und 2) könnte einint*int
Überlauf sein, der UB ist.quelle
powjii(INT_MAX, 63)
verursacht UB inbase *= base
. Überprüfen Sie, ob Sie multiplizieren können, oder wechseln Sie zu unsigniert und lassen Sie es umlaufen.exp
unterschrieben zu werden. Es verkompliziert den Code aufgrund der seltsamen Situation, in der er(-1) ** (-N)
gültig ist, und jederabs(base) > 1
gilt0
für negative Werte vonexp
. Daher ist es besser, ihn ohne Vorzeichen zu haben und diesen Code zu speichern.y
die Unterschrift nicht wirklich benötigt wird und die von Ihnen kommentierten Komplikationen mit sich bringt, aber die Anfrage von OP war spezifischpow(int, int)
. Somit gehören diese guten Kommentare zur Frage des OP. Da OP nicht angegeben hat, was bei Überlauf zu tun ist, ist eine genau definierte falsche Antwort nur unwesentlich besser als UB. Angesichts des "effizientesten Weges" bezweifle ich, dass OP sich um OF kümmert.allgemeinere Lösung unter Berücksichtigung negativer Exponenet
quelle
pow(i, INT_MIN)
könnte eine Endlosschleife sein.pow(i, INT_MIN)
ist kein ganzzahliger Überlauf. Die Zuordnung dieses Ergebnisses zutemp
kann sicherlich überlaufen, was möglicherweise das Ende der Zeit verursacht , aber ich werde mich mit einem scheinbar zufälligen Wert zufrieden geben. :-)Noch eine Implementierung (in Java). Möglicherweise nicht die effizienteste Lösung, aber die Anzahl der Iterationen entspricht der der Exponential-Lösung.
quelle
Ich benutze rekursiv, wenn die exp gerade ist, 5 ^ 10 = 25 ^ 5.
quelle
Zusätzlich zu der Antwort von Elias, die bei Implementierung mit vorzeichenbehafteten Ganzzahlen undefiniertes Verhalten und bei Implementierung mit vorzeichenlosen Ganzzahlen falsche Werte für hohe Eingaben verursacht,
Hier ist eine modifizierte Version der Exponentiation durch Quadrieren, die auch mit vorzeichenbehafteten Ganzzahltypen funktioniert und keine falschen Werte angibt:
Überlegungen zu dieser Funktion:
Wenn ein Überlauf oder eine Umhüllung stattfinden soll,
return 0;
Ich habe verwendet
int64_t
, aber jede Breite (signiert oder nicht signiert) kann mit nur geringen Änderungen verwendet werden. Wenn Sie jedoch einen nicht-feste Breite Integer - Typen verwenden müssen, werden Sie ändern müssen ,SQRT_INT64_MAX
um(int)sqrt(INT_MAX)
(in dem Fall der Verwendungint
) oder etwas ähnliches, das optimiert werden soll, aber es ist hässlicher, und kein C konstanter Ausdruck. Auch das Casting des Ergebnissessqrt()
auf einint
ist aufgrund der Gleitkomma-Präzision bei einem perfekten Quadrat nicht sehr gut, aber da ich keine Implementierung kenne, bei derINT_MAX
- oder das Maximum eines Typs - ein perfektes Quadrat ist, können Sie leben damit.quelle
Ich habe einen Algorithmus implementiert, der alle berechneten Potenzen speichert und sie dann bei Bedarf verwendet. So ist zum Beispiel x ^ 13 gleich (x ^ 2) ^ 2 ^ 2 * x ^ 2 ^ 2 * x, wobei x ^ 2 ^ 2 aus der Tabelle entnommen wird, anstatt es erneut zu berechnen. Dies ist im Grunde eine Implementierung der @ Pramod-Antwort (jedoch in C #). Die Anzahl der erforderlichen Multiplikationen ist Ceil (Log n).
quelle
public
? 2 gleichnamige Funktionen? Dies ist eine C-Frage.Mein Fall ist etwas anders, ich versuche, aus einer Kraft eine Maske zu erstellen, aber ich dachte, ich würde die Lösung teilen, die ich trotzdem gefunden habe.
Offensichtlich funktioniert es nur für Potenzen von 2.
quelle
#define MASK(e) (((e) >= 64) ? -1 :( (1 << (e)) - 1))
, die zur Kompilierungszeit berechnet werden könnenWenn Sie den Exponenten (und eine Ganzzahl) zur Kompilierungszeit kennen, können Sie die Schleife mithilfe von Vorlagen abrollen. Dies kann effizienter gestaltet werden, aber ich wollte hier das Grundprinzip demonstrieren:
Wir beenden die Rekursion mit einer Vorlagenspezialisierung:
Der Exponent muss zur Laufzeit bekannt sein.
quelle
(c != c++) == 1