Calcular la raíz cuadrada de un número en Python

En esta guía, aprenderá a calcular la raíz cuadrada de un número en Python, usando math.sqrt(), np.sqrt(), pow(), math.pow() y el operador **, con un punto de referencia de rendimiento.

Introducción

La raíz cuadrada de un número es una función matemática muy común utilizada en todos los aspectos de la ciencia: física, matemáticas, informática, etc. Las raíces cuadradas de números y expresiones son muy frecuentes en fórmulas en todos los asuntos de la ciencia, y especialmente en la forma en que representamos la realidad, modelando lo que podemos observar con el cálculo.

En este artículo, veremos varias formas de calcular la raíz cuadrada de un número en Python. Finalmente, haremos un Punto de referencia de rendimiento con números constantes y aleatorios, así como listas de números aleatorios para poner a prueba todos los enfoques.

Calcula la raíz cuadrada en Python con NumPy

NumPy es una biblioteca de computación científica, que se encontró presente en muchas aplicaciones y casos de uso. Naturalmente, tiene muchos envoltorios de funciones matemáticas como métodos auxiliares.

Si aún no está instalado, puede instalarlo a través de pip:

1
$ pip install numpy

En términos de NumPy, la función sqrt() calcula la raíz cuadrada de un número y devuelve el resultado:

1
2
3
import numpy as np
x = np.sqrt(2)
print(x)

Esto resulta en:

1
1.4142135623730951

Además de tomar una sola variable como argumento, sqrt() también puede analizar listas y devolver una lista de raíces cuadradas:

1
2
3
arr = [2, 3, 5, 7]
roots = np.sqrt(arr)
print(roots)

Esto resulta en:

1
[1.41421356 1.73205081 2.23606798 2.64575131]

Sin embargo, la función sqrt() tiene una limitación: no puede calcular la raíz cuadrada de un número negativo, porque la operación de raíz cuadrada con números reales solo se define para números positivos.

Intentar insertar -4 en la función sqrt() resultará en una excepción:

1
print(np.sqrt(-4))

Intentar calcular la raíz cuadrada de un número negativo dará como resultado una advertencia y un valor nan:

1
2
RuntimeWarning: invalid value encountered in sqrt
nan

Calcular la raíz cuadrada de un número complejo con Numpy

Afortunadamente, NumPy no está limitado a trabajar solo con números reales, también puede funcionar con números complejos:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
import numpy as np

complex_number = -1 + 1j
complex_array = [-2, 3, complex_number]

complex_root = np.sqrt(complex_number)
complex_array_roots = np.sqrt(complex_array)

print(f"Square root of '{complex_number}':\n {complex_root}")
print(f"Square roots of '{complex_array}':\n {complex_array_roots}")

Si hay al menos un número complejo en una lista, todos los números se convertirán y se tratarán como complejos, por lo que incluso se pueden agregar números enteros negativos:

1
2
3
4
Square root of '(-1+1j)':
 (0.45508986056222733+1.09868411346781j)
Square roots of '[-2, 3, (-1+1j)]':
 [0.        +1.41421356j 1.73205081+0.j         0.45508986+1.09868411j]

Módulo matemático de Python

El módulo math es un módulo estándar empaquetado con Python. Siempre está disponible, pero debe importarse y proporciona contenedores para algunas funciones comunes, como la raíz cuadrada, potencias, etc.:

1
import math
matemáticas.sqrt()

La función sqrt() del módulo math es una función sencilla que devuelve la raíz cuadrada de cualquier número positivo:

1
print(math.sqrt(2))

Esto resulta en:

1
1.4142135623730951

A diferencia de la función sqrt() de NumPy, solo puede funcionar en un solo elemento, por lo que si desea calcular la raíz cuadrada de todos los elementos en una lista, tendrá que usar un bucle for o una lista de comprensión:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
import math

arr = [2, 3, 5, 7]
roots = []

for x in arr:
    roots.append(math.sqrt(x))

# OR
roots = [math.sqrt(x) for x in arr]

En ambos casos, la lista raíces contendrá:

1
[1.4142135623730951, 1.7320508075688772, 2.23606797749979, 2.6457513110645907]
math.pow()

La raíz cuadrada de un número también se puede calcular elevando un número a una potencia de ½:

$$
\raíz cuadrada x = x^{\frac 1 2}
$$

Realmente, encontrar la raíz cuadrada de un número se puede expresar como elevar el número a una potencia de ½. math.pow() toma dos argumentos: la base y el exponente, y eleva la base a la potencia de un exponente:

1
print(math.pow(2, 0.5))

Naturalmente, esto resulta en:

1
1.4142135623730951

El operador **

El operador ** es un operador binario, lo que significa que funciona con dos valores, al igual que la multiplicación regular con *. Sin embargo, como es un operador usado para exponenciación, elevamos su argumento izquierdo a la potencia de su argumento derecho.

Este enfoque se puede utilizar de la misma forma que el anterior:

1
print(2 ** 0.5)

Y también resulta en:

1
1.4142135623730951

La función pow()

Python tiene otro método pow() incorporado que no requiere una importación del módulo math. Este método es técnicamente diferente del método math.pow() internamente.

math.pow() implícitamente convierte elementos en dobles, mientras que pow() usa la implementación interna del objeto, basada en el operador **. Si bien esta diferencia en la implementación puede justificar el uso de uno u otro en ciertos contextos, si solo está calculando la raíz cuadrada de un número, realmente no verá la diferencia:

1
print(pow(2, 0.5))

Esto resulta en:

1
1.4142135623730951

Punto de referencia de rendimiento {#punto de referencia de rendimiento}

Entonces, ¿cuál produce el mejor rendimiento y cuál debería elegir? Como de costumbre, no hay un ganador claro y depende del uso de los métodos. Es decir, si está trabajando con números constantes, números aleatorios o una matriz de números aleatorios a mayor escala, estos métodos funcionarán de manera diferente.

Vamos a probarlos todos con números constantes, números aleatorios y matrices de números aleatorios:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
import timeit

print("Time to execute 100k operations on constant number: \n")
print("math.sqrt(): %ss" % timeit.timeit("math.sqrt(100)", setup="import math", number=100000))
print("math.pow(): %ss" % timeit.timeit("math.pow(100, 0.5)", setup="import math", number=100000))
print("pow(): %ss" % timeit.timeit("pow(100, 0.5)", number=100000))
print("np.sqrt(): %ss" % timeit.timeit("np.sqrt(100)", setup="import numpy as np", number=100000))
print("** operator: %ss" % timeit.timeit("100 ** 0.5", number=100000))

print("\nTime to execute 100k operations on random number: \n")
print("math.sqrt() %ss" % timeit.timeit("math.sqrt(random.random())", setup="import math; import random;", number=100000))
print("math.pow(): %ss" % timeit.timeit("math.pow(random.random(), 0.5)", setup="import math; import random", number=100000))
print("pow(): %ss" % timeit.timeit("pow(random.random(), 0.5)", setup="import random", number=100000))
print("np.sqrt(): %ss" % timeit.timeit("np.sqrt(random.random())", setup="import numpy as np; import random", number=100000))
print("** operator: %ss" % timeit.timeit("random.random() ** 0.5", setup="import random", number=100000))

print("\nTime to execute 100k operations on list of random numbers: \n")
print("math.sqrt() %ss" % timeit.timeit("[math.sqrt(x) for x in np.random.rand(100)]", setup="import math; import numpy as np;", number=100000))
print("math.pow(): %ss" % timeit.timeit("[math.pow(x, 0.5) for x in np.random.rand(100)]", setup="import math; import numpy as np;", number=100000))
print("pow(): %ss" % timeit.timeit("[pow(x, 0.5) for x in np.random.rand(100)]", setup="import numpy as np;", number=100000))
print("np.sqrt(): %ss" % timeit.timeit("np.sqrt(np.random.rand(100))", setup="import numpy as np; import numpy as np;", number=100000))
print("** operator: %ss" % timeit.timeit("np.random.rand(100) ** 0.5", setup="import numpy as np", number=100000))

Pasamos todos los métodos descritos anteriormente a través de la misma prueba: un número constante (que probablemente se almacene en caché para la optimización), un número aleatorio en cada una de las 100k iteraciones y una lista de 100 números aleatorios.

{.icon aria-hidden=“true”}

Nota: Solo los números relativos en cada prueba en comparación con otros métodos en esa prueba son relevantes, ya que lleva más tiempo generar 100 números aleatorios que usar el valor constante (almacenado en caché).

Ejecutar este fragmento de código da como resultado:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
Time to execute 100k operations on constant number: 

math.sqrt(): 0.014326499999999999s
math.pow(): 0.0165132s
pow(): 0.018766599999999994s
np.sqrt(): 0.10575379999999998s
** operator: 0.0006493000000000193s

Time to execute 100k operations on random number: 

math.sqrt() 0.019939999999999958s
math.pow(): 0.022284300000000035s
pow(): 0.0231711s
np.sqrt(): 0.09066460000000004s
** operator: 0.018928s

Time to execute 100k operations on list of random numbers: 

math.sqrt() 2.7786073s
math.pow(): 2.9986906s
pow(): 3.5157339999999992s 
np.sqrt(): 0.2291957s
** operator: 0.2376024000000001s

Con números constantes, las funciones math.pow(), math.sqrt() y pow() superan significativamente a la función sqrt() de NumPy, ya que pueden utilizar mejor el almacenamiento en caché en la CPU en el nivel de idioma.

Con números aleatorios, el almacenamiento en caché no funciona tampoco y vemos discrepancias más pequeñas.

Con listas de números aleatorios, np.sqrt() supera a los tres métodos incorporados significativamente, y el operador ** funciona en el mismo estadio.

Para resumir:

{.icon aria-hidden=“true”}

  • Para números constantes, el operador ** claramente se desempeña mejor en la máquina de prueba, ejecutándose 16 veces más rápido que los métodos integrados.
  • Para números aleatorios, np.sqrt() supera a los métodos integrados, y el operador **, sin embargo, no hay una discrepancia significativa en los resultados.
  • Para matrices aleatorias, la función np.sqrt() supera a los métodos integrados, pero el operador ** está muy cerca.

Dependiendo de la entrada concreta con la que esté tratando, podrá elegir entre estas funciones. Si bien puede parecer que todos funcionarán bien, y aunque en la mayoría de los casos no supondrá una gran diferencia, cuando se trata de grandes conjuntos de datos, incluso una reducción del 10 % en el tiempo de procesamiento puede ayudar. el largo plazo.

Dependiendo de los datos que esté procesando, pruebe los diferentes enfoques en su máquina local.

Conclusión

En este breve artículo, echamos un vistazo a varias formas de calcular la raíz cuadrada de un número en Python.

Hemos echado un vistazo a las funciones pow() y sqrt() del módulo math, así como a la función pow() integrada, sqrt( de NumPy\'s ) y el operador **. Finalmente, hemos evaluado los métodos para comparar su rendimiento en diferentes tipos de entrada: números constantes, números aleatorios y listas de números aleatorios. ios.

Licensed under CC BY-NC-SA 4.0