Necesito calcular la raíz cuadrada de algunos números, por ejemplo √9 = 3
y √2 = 1.4142
. ¿Cómo puedo hacerlo en Python?
Las entradas probablemente serán todos números enteros positivos y relativamente pequeños (digamos menos de mil millones), pero en caso de que no lo sean, ¿hay algo que pueda romperse?
Relacionados
Nota : Este es un intento de una pregunta canónica después de una discusión en Meta sobre una pregunta existente con el mismo título .
>>> import numpy as np >>> np.sqrt(25) 5.0 >>> np.sqrt([2, 3, 4]) array([1.41421356, 1.73205081, 2. ])
Para reales negativos, devolverá nan
, por lo que np.lib.scimath.sqrt()
está disponible para ese caso.
>>> a = np.array([4, -1, np.inf]) >>> np.sqrt(a) <stdin>:1: RuntimeWarning: invalid value encountered in sqrt array([ 2., nan, inf]) >>> np.lib.scimath.sqrt(a) array([ 2.+0.j, 0.+1.j, inf+0.j])
Otra opción, por supuesto, es convertir primero a complejo:
>>> a = a.astype(complex) >>> np.sqrt(a) array([ 2.+0.j, 0.+1.j, inf+0.j])
Dependiendo de tu objetivo, podría ser una buena idea retrasar el cálculo de las raíces cuadradas tanto como sea posible. SymPy podría ayudar.
SymPy es una biblioteca de Python para matemáticas simbólicas.
import sympy sympy.sqrt(2) # => sqrt(2)
Esto no parece muy útil al principio.
Pero sympy puede dar más información que flotantes o decimales:
sympy.sqrt(8) / sympy.sqrt(27) # => 2*sqrt(6)/9
Además, no se pierde precisión. (√2)² sigue siendo un número entero:
s = sympy.sqrt(2) s**2 # => 2 type(s**2) #=> <class 'sympy.core.numbers.Integer'>
En comparación, los flotantes y los decimales devolverían un número muy cercano a 2 pero no igual a 2:
(2**0.5)**2 # => 2.0000000000000004 from decimal import Decimal (Decimal('2')**Decimal('0.5'))**Decimal('2') # => Decimal('1.999999999999999999999999999')
Sympy también comprende ejemplos más complejos como la integral de Gauss :
from sympy import Symbol, integrate, pi, sqrt, exp, oo x = Symbol('x') integrate(exp(-x**2), (x, -oo, oo)) # => sqrt(pi) integrate(exp(-x**2), (x, -oo, oo)) == sqrt(pi) # => True
Finalmente, si se desea una representación decimal, es posible solicitar más dígitos de los que se necesitarán:
sympy.N(sympy.sqrt(2), 1_000_000) # => 1.4142135623730950488016...........2044193016904841204
Descargo de responsabilidad: esto es para un caso de uso más especializado. Este método podría no ser práctico en todas las circunstancias.
Beneficios:
Personalmente implementé este para un desafío criptográfico CTF (ataque de raíz cúbica RSA), donde necesitaba un valor entero preciso.
La idea general puede extenderse a cualquier otra raíz.
def int_squareroot(d: int) -> tuple[int, bool]: """Try calculating integer squareroot and return if it's exact""" left, right = 1, (d+1)//2 while left<right-1: x = (left+right)//2 if x**2 > d: left, right = left, x else: left, right = x, right return left, left**2==d
Como @wjandrea también ha señalado, ** este código de ejemplo NO puede calcular **. Este es un efecto secundario del hecho de que no convierte nada en flotantes, por lo que no se pierde precisión. Si la raíz es un número entero, lo recuperas. Si no es así, obtienes el número más grande cuyo cuadrado es más pequeño que tu número. Actualicé el código para que también devuelva un bool que indica si el valor es correcto o no, y también arreglé un problema que causaba un bucle infinito (también señalado por @wjandrea). Esta implementación del método general todavía funciona un poco raro para números más pequeños, pero por encima de 10 no tuve problemas.
Para números más pequeños, puede usar todos los demás métodos de otras respuestas. Por lo general, usan flotantes, lo que podría ser una pérdida de precisión, pero para números enteros pequeños eso no debería significar ningún problema. Todos esos métodos que usan flotantes tienen el mismo (o casi el mismo) límite de esto.
Si aún desea usar este método y obtener resultados flotantes, debería ser trivial convertir esto para usar también flotantes. Tenga en cuenta que eso reintroducirá la pérdida de precisión, el beneficio único de este método sobre los demás, y en ese caso también puede usar cualquiera de las otras respuestas. Creo que la versión del método de Newton converge un poco más rápido, pero no estoy seguro.
Para números más grandes, donde entra en juego la pérdida de precisión con los flotantes, este método puede dar resultados más cercanos a la respuesta real (dependiendo de qué tan grande sea la entrada). Si desea trabajar con números no enteros en este rango, puede usar otros tipos, por ejemplo, números de precisión fijos también en este método.
Actualmente, y afaik, la única otra respuesta que tiene una precisión similar o mejor para números grandes que esta implementación es la que sugiere SymPy, de Eric Duminil. Esa versión también es más fácil de usar y funciona para cualquier tipo de número, el único inconveniente es que requiere SymPy. Mi implementación está libre de dependencias enormes si eso es lo que está buscando.
El módulo de fractions
de Python y su clase, Fraction
, implementan la aritmética con números racionales. La clase Fraction
no implementa una operación de raíz cuadrada, porque la mayoría de las raíces cuadradas son números irracionales. Sin embargo, se puede usar para aproximar una raíz cuadrada con precisión arbitraria, porque el numerador y el denominador de una Fraction
son números enteros de precisión arbitraria.
El siguiente método toma un número positivo x
y un número de iteraciones, y devuelve los límites superior e inferior para la raíz cuadrada de x
.
from fractions import Fraction def sqrt(x, n): x = x if isinstance(x, Fraction) else Fraction(x) upper = x + 1 for i in range(0, n): upper = (upper + x/upper) / 2 lower = x / upper if lower > upper: raise ValueError("Sanity check failed") return (lower, upper)
Consulte la referencia a continuación para obtener detalles sobre la implementación de esta operación. También muestra cómo implementar otras operaciones con límites superior e inferior (aunque aparentemente hay al menos un error con la operación de log
allí).
La forma más simple y precisa de calcular la raíz cuadrada es el método de Newton.
Tiene un número del que desea calcular su raíz cuadrada ( num
) y tiene una suposición de su raíz cuadrada ( estimate
). La estimación puede ser cualquier número mayor que 0, pero un número que tenga sentido acorta significativamente la profundidad de la llamada recursiva.
new_estimate = (estimate + num/estimate) / 2
Esta línea calcula una estimación más precisa con esos 2 parámetros. Puede pasar el valor new_estimate
a la función y calcular otro new_estimate
que sea más preciso que el anterior o puede hacer una definición de función recursiva como esta.
def newtons_method(num, estimate): # Computing a new_estimate new_estimate = (estimate + num/estimate) / 2 print(new_estimate) # Base Case: Comparing our estimate with built-in functions value if new_estimate == math.sqrt(num): return True else: return newtons_method(num, new_estimate)
Por ejemplo, necesitamos encontrar la raíz cuadrada de 30. Sabemos que el resultado está entre 5 y 6.
newtons_method(30,5)
el número es 30 y la estimación es 5. El resultado de cada llamada recursiva es:
5.5 5.477272727272727 5.4772255752546215 5.477225575051661
El último resultado es el cálculo más preciso de la raíz cuadrada de un número. Es el mismo valor que la función math.sqrt()
.
Esta respuesta fue publicada originalmente por gunesevitan , pero ahora se eliminó.
Esta variación utiliza manipulaciones de cadenas para convertir una cadena que representa un número de coma flotante decimal en un int
, llama a math.isqrt
para realizar la extracción real de la raíz cuadrada y luego formatea el resultado como una cadena decimal. math.isqrt
redondea hacia abajo, por lo que todos los dígitos producidos son correctos.
La cadena de entrada, num
, debe usar un formato flotante simple: no se admite la notación 'e'. La cadena num
puede ser un entero simple y los ceros iniciales se ignoran.
El argumento digits
especifica el número de lugares decimales en la cadena de resultados, es decir, el número de dígitos después del punto decimal.
from math import isqrt def str_sqrt(num, digits): """ Arbitrary precision square root num arg must be a string Return a string with `digits` after the decimal point Written by PM 2Ring 2022.01.26 """ int_part , _, frac_part = num.partition('.') num = int_part + frac_part # Determine the required precision width = 2 * digits - len(frac_part) # Truncate or pad with zeroes num = num[:width] if width < 0 else num + '0' * width s = str(isqrt(int(num))) if digits: # Pad, if necessary s = '0' * (1 + digits - len(s)) + s s = f"{s[:-digits]}.{s[-digits:]}" return s
print(str_sqrt("2.0", 30))
1.414213562373095048801688724209
Para cantidades pequeñas de dígitos, es más rápido usar decimal.Decimal.sqrt
. Alrededor de 32 dígitos, str_sqrt
tiene aproximadamente la misma velocidad que Decimal.sqrt
. Pero con 128 dígitos, str_sqrt
es 2,2 veces más rápido que Decimal.sqrt
, con 512 dígitos, es 4,3 veces más rápido, con 8192 dígitos, es 7,4 veces más rápido.
Aquí hay una versión en vivo que se ejecuta en el servidor SageMathCell.