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 .
math.sqrt() El módulo math de la biblioteca estándar tiene una función sqrt para calcular la raíz cuadrada de un número. Toma cualquier tipo que se pueda convertir a float (lo que incluye int ) como argumento y devuelve un float .
>>> import math >>> math.sqrt(9) 3.0 El operador de potencia ( ** ) o la función integrada pow() también se pueden usar para calcular una raíz cuadrada. Matemáticamente hablando, la raíz cuadrada de a es igual a a elevado a 1/2 .
El operador de potencia requiere tipos numéricos y coincide con las reglas de conversión para operadores aritméticos binarios , por lo que en este caso devolverá un número float o un número complex .
>>> 9 ** (1/2) 3.0 >>> 9 ** .5 # Same thing 3.0 >>> 2 ** .5 1.4142135623730951 (Nota: en Python 2, 1/2 se trunca a 0 , por lo que debe forzar la aritmética de punto flotante con 1.0/2 o similar. Consulte ¿Por qué Python da la respuesta "incorrecta" para la raíz cuadrada? )
Este método se puede generalizar a la raíz enésima , aunque las fracciones que no se pueden representar exactamente como un float (como 1/3 o cualquier denominador que no sea una potencia de 2) pueden causar cierta inexactitud:
>>> 8 ** (1/3) 2.0 >>> 125 ** (1/3) 4.999999999999999La exponenciación funciona con números negativos y números complejos, aunque los resultados tienen una ligera inexactitud:
>>> (-25) ** .5 # Should be 5j (3.061616997868383e-16+5j) >>> 8j ** .5 # Should be 2+2j (2.0000000000000004+2j) ¡Observe los paréntesis en -25 ! De lo contrario, se analiza como -(25**.5) porquela exponenciación es más vinculante que la negación unaria .
Mientras tanto, las math solo se crean para flotantes, por lo que para x<0 , math.sqrt() generará ValueError: math domain error y para x complejo, generará TypeError: can't convert complex to float . En su lugar, puede usar cmath.sqrt() , que es más preciso que la exponenciación (y probablemente también sea más rápido):
>>> import cmath >>> cmath.sqrt(-25) 5j >>> cmath.sqrt(8j) (2+2j) Ambas opciones implican una conversión implícita a float , por lo que la precisión de la coma flotante es un factor . Por ejemplo:
>>> n = 10**30 >>> square = n**2 >>> x = square**.5 >>> x == n False >>> x - n # how far off are they? 0.0 >>> int(x) - n # how far off is the float from the int? 19884624838656 Es posible que los números muy grandes ni siquiera quepan en un flotante y obtendrá OverflowError: int too large to convert to float . ¿Ve el límite de Python sqrt para números muy grandes?
Veamos Decimal por ejemplo:
La exponenciación falla a menos que el exponente también sea Decimal :
>>> decimal.Decimal('9') ** .5 Traceback (most recent call last): File "<stdin>", line 1, in <module> TypeError: unsupported operand type(s) for ** or pow(): 'decimal.Decimal' and 'float' >>> decimal.Decimal('9') ** decimal.Decimal('.5') Decimal('3.000000000000000000000000000') Mientras tanto, math y cmath convertirán silenciosamente sus argumentos en float y complex respectivamente, lo que podría significar una pérdida de precisión.
decimal también tiene su propio .sqrt() . Ver también cálculo de raíces n-ésimas usando el módulo decimal de Python 3
>>> 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)/9Ademá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) # => TrueFinalmente, 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...........2044193016904841204Descargo 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==dComo @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.