Saltar al contenido

Exponenciales en python x. ** y vs math.pow (x, y)

Solución:

Usando el operador de energía ** será más rápido ya que no tendrá la sobrecarga de una llamada de función. Puede ver esto si desmonta el código Python:

>>> dis.dis('7. ** i')
  1           0 LOAD_CONST               0 (7.0) 
              3 LOAD_NAME                0 (i) 
              6 BINARY_POWER         
              7 RETURN_VALUE         
>>> dis.dis('pow(7., i)')
  1           0 LOAD_NAME                0 (pow) 
              3 LOAD_CONST               0 (7.0) 
              6 LOAD_NAME                1 (i) 
              9 CALL_FUNCTION            2 (2 positional, 0 keyword pair) 
             12 RETURN_VALUE         
>>> dis.dis('math.pow(7, i)')
  1           0 LOAD_NAME                0 (math) 
              3 LOAD_ATTR                1 (pow) 
              6 LOAD_CONST               0 (7) 
              9 LOAD_NAME                2 (i) 
             12 CALL_FUNCTION            2 (2 positional, 0 keyword pair) 
             15 RETURN_VALUE         

Tenga en cuenta que estoy usando una variable i como exponente aquí porque expresiones constantes como 7. ** 5 se evalúan realmente en tiempo de compilación.

Ahora bien, en la práctica, esta diferencia no importa tanto, como puede ver al cronometrarla:

>>> from timeit import timeit
>>> timeit('7. ** i', setup='i = 5')
0.2894785532627111
>>> timeit('pow(7., i)', setup='i = 5')
0.41218495570683444
>>> timeit('math.pow(7, i)', setup='import math; i = 5')
0.5655053168791255

Entonces, mientras pow y math.pow son aproximadamente el doble de lentos, pero aún así son lo suficientemente rápidos como para no importarles mucho. A menos que pueda identificar la exponenciación como un cuello de botella, no habrá razón para elegir un método sobre el otro si la claridad disminuye. Esto se aplica especialmente desde pow ofrece una operación de módulo integrada, por ejemplo.


Alfe hizo una buena pregunta en los comentarios anteriores:

timeit muestra que math.pow es más lento que ** en todos los casos. Que es math.pow() bueno de todos modos? ¿Alguien tiene una idea de dónde puede ser de alguna ventaja entonces?

La gran diferencia de math.pow tanto para el incorporado pow y el operador de energía ** es asi siempre utiliza semántica flotante. Entonces, si, por alguna razón, desea asegurarse de obtener un flotador como resultado, entonces math.pow asegurará esta propiedad.

Pensemos en un ejemplo: tenemos dos números, i y jy no tengo idea de si son números enteros o flotantes. Pero queremos tener un resultado flotante de i^j. Entonces, ¿qué opciones tenemos?

  • Podemos convertir al menos uno de los argumentos a un flotante y luego hacer i ** j.
  • Podemos hacer i ** j y convertir el resultado en un flotador (la exponencia del flotador se usa automáticamente cuando i o j son flotantes, por lo que el resultado es el mismo).
  • Nosotros podemos usar math.pow.

Entonces, probemos esto:

>>> timeit('float(i) ** j', setup='i, j = 7, 5')
0.7610865891750791
>>> timeit('i ** float(j)', setup='i, j = 7, 5')
0.7930400942188385
>>> timeit('float(i ** j)', setup='i, j = 7, 5')
0.8946636625872202
>>> timeit('math.pow(i, j)', setup='import math; i, j = 7, 5')
0.5699394063529439

Como se puede ver, math.pow es en realidad más rápido! Y si lo piensa, la sobrecarga de la llamada a la función también se ha ido ahora, porque en todas las otras alternativas tenemos que llamar float().


Además, podría valer la pena señalar que el comportamiento de ** y pow se puede anular mediante la implementación de la especial __pow__ (y __rpow__) método para tipos personalizados. Entonces, si no quieres eso (por el motivo que sea), usa math.pow no haré eso.

La función pow () le permitirá agregar un tercer argumento como módulo.

Por ejemplo: recientemente me enfrenté a un error de memoria al hacer

2 ** 23375247598357347582% 23375247598357347583

En cambio lo hice:

pow(2, 23375247598357347582, 23375247598357347583)

Esto regresa en meros milisegundos en lugar de la enorme cantidad de tiempo y memoria que toma el exponente simple. Entonces, cuando se trata de números grandes y módulo paralelo, pow () es más eficiente, sin embargo, cuando se trata de números más pequeños sin módulo, ** es más eficiente.

Solo por el protocolo: El ** el operador llama al incorporado pow función que acepta un tercer argumento opcional (módulo) si los dos primeros argumentos son tipos enteros.

Entonces, si tiene la intención de calcular los residuos de las potencias, use la función incorporada. los math.pow puede darle resultados falsos:

import math

base = 13
exp = 100
mod = 2
print math.pow(base, exp) % mod
print pow(base, exp, mod)

Cuando ejecuté esto, obtuve 0.0 en el primer caso, lo que obviamente no puede ser cierto, porque 13 es impar (y por lo tanto todos sus poderes integrales). los math.pow La versión utiliza una precisión limitada que provoca un error.

En aras de la justicia, debemos decir: math.pow puede ser mucho más rápido:

import timeit
print timeit.timeit("math.pow(2, 100)",setup='import math')
print timeit.timeit("pow(2, 100)")

Esto es lo que obtengo como salida:

0.240936803195
1.4775809183

Algunos ejemplos en línea

  • http://ideone.com/qaDWRd (resto incorrecto con math.pow)
  • http://ideone.com/g7J9Un (menor rendimiento con pow en valores int)
  • http://ideone.com/KnEtXj (rendimiento ligeramente inferior con pow en valores flotantes)
¡Haz clic para puntuar esta entrada!
(Votos: 0 Promedio: 0)



Utiliza Nuestro Buscador

Deja una respuesta

Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *