¿Cuál es la forma más eficiente de elevar un número entero a la potencia de otro número entero en C?
// 2^3
pow(2,3) == 8
// 5^5
pow(5,5) == 3125
c
algorithm
math
exponentiation
Doug T.
fuente
fuente
int
s real (y no a una clase de int. Enorme), se desbordarán muchas llamadas a ipow. Me hace preguntarme si hay una manera inteligente de calcular previamente una tabla y reducir todas las combinaciones que no se desbordan a una simple búsqueda de tabla. Esto requeriría más memoria que la mayoría de las respuestas generales, pero quizás sea más eficiente en términos de velocidad.pow()
no es una función seguraRespuestas:
Exponenciación por cuadratura.
Este es el método estándar para hacer exponenciación modular para grandes cantidades en criptografía asimétrica.
fuente
while (exp)
yif (exp & 1)
conwhile (exp != 0)
yif ((exp & 1) != 0)
respectivamente.unsigned exp
, o bien manejar negativamenteexp
correctamente.n*n*n*n*n*n*n*n
utiliza 7 multiplicaciones. Este algoritmo calculam=n*n
, entonceso=m*m
, entoncesp=o*o
, dondep
= n ^ 8, con solo tres multiplicaciones. Con grandes exponentes, la diferencia en el rendimiento es significativa.Tenga en cuenta que la exponenciación al cuadrado no es el método más óptimo. Probablemente sea lo mejor que puede hacer como método general que funciona para todos los valores de exponente, pero para un valor de exponente específico podría haber una secuencia mejor que necesita menos multiplicaciones.
Por ejemplo, si desea calcular x ^ 15, el método de exponenciación al cuadrado le dará:
Esto es un total de 6 multiplicaciones.
Resulta que esto se puede hacer usando "solo" 5 multiplicaciones a través de la exponenciación de la cadena de suma .
No hay algoritmos eficientes para encontrar esta secuencia óptima de multiplicaciones. De Wikipedia :
fuente
Si necesitas subir 2 a una potencia. La forma más rápida de hacerlo es cambiar un poco el poder.
fuente
2 ** 0 == 1 << 0 == 1
Aquí está el método en Java
fuente
fuente
pow(1, -1)
no deja el rango de int a pesar de un exponente negativo. Ahora que uno trabaja por accidente, como lo hacepow(-1, -1)
.Si desea obtener el valor de un entero para 2 elevado a la potencia de algo, siempre es mejor usar la opción shift:
pow(2,5)
puede ser reemplazado por1<<5
Esto es mucho más eficiente.
fuente
power()
función para trabajar solo para enterosComplejidad = O (log (exp))
power()
función para trabajar para exp negativo y base flotante .Complejidad = O (log (exp))
fuente
float
en el segundo bloque de código presentado (considere mostrar cómopower(2.0, -3)
se calcula).negative exp and float base
solución? ¿Por qué usamos temp, separamos exp por 2 y verificamos exp (par / impar)? ¡Gracias!Un caso extremadamente especializado es, cuando necesita decir 2 ^ (- x a la y), donde x, por supuesto, es negativo e y es demasiado grande para hacer un cambio en un int. Todavía puedes hacer 2 ^ x en tiempo constante atornillando con un flotador.
Puedes obtener más potencias de 2 usando un doble como tipo base. (Muchas gracias a los comentaristas por ayudar a corregir esta publicación).
También existe la posibilidad de que al aprender más sobre las carrozas IEEE , se presenten otros casos especiales de exponenciación.
fuente
Solo como seguimiento de los comentarios sobre la eficiencia de la exponenciación mediante la cuadratura.
La ventaja de este enfoque es que se ejecuta en tiempo log (n). Por ejemplo, si iba a calcular algo enorme, como x ^ 1048575 (2 ^ 20 - 1), solo tiene que pasar por el bucle 20 veces, no 1 millón + usando el enfoque ingenuo.
Además, en términos de complejidad del código, es más simple que tratar de encontrar la secuencia más óptima de multiplicaciones, a sugerencia de Pramod.
Editar:
Creo que debería aclarar antes de que alguien me etiquete por el potencial de desbordamiento. Este enfoque supone que tiene algún tipo de biblioteca de gran tamaño.
fuente
Tarde a la fiesta:
A continuación se muestra una solución que también trata de la
y < 0
mejor manera posible.intmax_t
para el rango máximo. No hay disposición para respuestas que no encajanintmax_t
.powjii(0, 0) --> 1
lo cual es un resultado común para este caso.pow(0,negative)
, otro resultado indefinido, devuelveINTMAX_MAX
Este código usa un bucle
for(;;)
para siempre para evitar elbase *= base
común final en otras soluciones en bucle. Esa multiplicación es 1) no necesaria y 2) podría serint*int
desbordamiento, que es UB.fuente
powjii(INT_MAX, 63)
causa UB enbase *= base
. Considere verificar que puede multiplicar o pasar a sin firmar y dejar que se ajuste.exp
sido firmado. Complica el código debido a la extraña situación en la que(-1) ** (-N)
es válido, y cualquieraabs(base) > 1
será0
para valores negativos deexp
, por lo que es mejor tenerlo sin firmar y guardar ese código.y
tal como se firmó, no es realmente necesario y trae las complicaciones que comentó, pero la solicitud de OP fue específicapow(int, int)
. Por lo tanto, esos buenos comentarios pertenecen a la pregunta del OP. Como OP no ha especificado qué hacer en caso de desbordamiento, una respuesta incorrecta bien definida es solo marginalmente mejor que UB. Dada la "forma más eficiente", dudo que a OP le importe OF.solución más genérica teniendo en cuenta el exponenet negativo
fuente
pow(i, INT_MIN)
podría ser un bucle infinito.pow(i, INT_MIN)
no es un desbordamiento de enteros. La asignación de ese resultadotemp
ciertamente puede desbordarse, lo que puede causar el fin del tiempo , pero me conformaré con un valor aparentemente aleatorio. :-)Una implementación más (en Java). Puede que no sea la solución más eficiente, pero el número de iteraciones es el mismo que el de la solución exponencial.
fuente
Yo uso recursivo, si el exp es par, 5 ^ 10 = 25 ^ 5.
fuente
Además de la respuesta de Elias, que causa un comportamiento indefinido cuando se implementa con enteros con signo, y valores incorrectos para una entrada alta cuando se implementa con enteros sin signo,
Aquí hay una versión modificada de Exponentiation by Squaring que también funciona con tipos enteros con signo y no da valores incorrectos:
Consideraciones para esta función:
Si se va a producir un desbordamiento o envoltura,
return 0;
Solía
int64_t
, pero cualquier ancho (con o sin signo) se puede usar con poca modificación. Sin embargo, si necesita usar un tipo entero de ancho no fijo, deberá cambiarSQRT_INT64_MAX
por(int)sqrt(INT_MAX)
(en el caso de usarint
) o algo similar, que debe optimizarse, pero es más feo y no una expresión constante C. Además, emitir el resultado desqrt()
aint
no es muy bueno debido a la precisión de coma flotante en el caso de un cuadrado perfecto, pero como no conozco ninguna implementación donde,INT_MAX
o el máximo de cualquier tipo, sea un cuadrado perfecto, puedes vivir con ese.fuente
He implementado un algoritmo que memoriza todas las potencias calculadas y luego las usa cuando es necesario. Entonces, por ejemplo, x ^ 13 es igual a (x ^ 2) ^ 2 ^ 2 * x ^ 2 ^ 2 * x donde x ^ 2 ^ 2 se tomó de la tabla en lugar de calcularlo nuevamente. Esto es básicamente la implementación de la respuesta @Pramod (pero en C #). La cantidad de multiplicación necesaria es Ceil (Log n)
fuente
public
? 2 funciones con el mismo nombre? Esta es una pregunta C.Mi caso es un poco diferente, estoy tratando de crear una máscara a partir de un poder, pero pensé en compartir la solución que encontré de todos modos.
Obviamente, solo funciona para potencias de 2.
fuente
#define MASK(e) (((e) >= 64) ? -1 :( (1 << (e)) - 1))
, por lo que se pueden calcular en tiempo de compilaciónEn caso de que conozca el exponente (y es un número entero) en tiempo de compilación, puede usar plantillas para desenrollar el bucle. Esto puede hacerse más eficiente, pero quería demostrar el principio básico aquí:
Terminamos la recursión usando una especialización de plantilla:
El exponente necesita ser conocido en tiempo de ejecución,
fuente
(c != c++) == 1