如何计算 Python 的平方根?
How do I calculate square root in Python?
我需要计算一些数字的平方根,例如√9 = 3
和√2 = 1.4142
。我怎样才能在 Python 中做到这一点?
输入可能都是正整数,并且相对较小(比如小于十亿),但如果不是,是否有任何可能会损坏的东西?
相关
- Integer square root in python
- How to find integer nth roots?
- Is there a short-hand for nth root of x in Python?
- Python sqrt limit for very large numbers?
- Which is faster in Python: x**.5 or math.sqrt(x)?
- Why does Python give the "wrong" answer for square root?(具体到Python2)
- calculating n-th roots using Python 3's decimal module
- How can I take the square root of -1 using python?(专注于 NumPy)
- Arbitrary precision of square roots
注意:这是对canonical question after a discussion on Meta about an existing question with the same title.[的尝试=25=]
选项 1:math.sqrt()
标准库中的 math
模块将 a sqrt
function to calculate the square root of a number. It takes any type that can be converted to float
(包括 int
)作为参数,并且 return 是一个 float
.
>>> import math
>>> math.sqrt(9)
3.0
选项 2:分数指数
The power operator (**
) or the built-in pow()
function can also be used to calculate a square root. Mathematically speaking, the square root of a
equals a
to the power of 1/2
.
幂运算符需要数字类型并匹配 the conversion rules for binary arithmetic operators,因此在本例中它将 return 为 float
或 complex
数字。
>>> 9 ** (1/2)
3.0
>>> 9 ** .5 # Same thing
3.0
>>> 2 ** .5
1.4142135623730951
(注意:在 Python 2 中,1/2
被截断为 0
,因此您必须使用 1.0/2
或类似的强制浮点运算。参见 Why does Python give the "wrong" answer for square root?)
此方法可以推广到 nth root,但不能精确表示为 float
的分数(例如 1/3 或任何不是 2 的幂的分母)可能会导致一些不准确的地方:
>>> 8 ** (1/3)
2.0
>>> 125 ** (1/3)
4.999999999999999
边缘情况
消极且复杂
求幂适用于负数和复数,但结果略有不准确:
>>> (-25) ** .5 # Should be 5j
(3.061616997868383e-16+5j)
>>> 8j ** .5 # Should be 2+2j
(2.0000000000000004+2j)
注意 -25
上的括号!否则它被解析为 -(25**.5)
因为 exponentiation is more tightly binding than unary negation.
与此同时,math
仅适用于浮点数,因此对于 x<0
,math.sqrt()
将提高 ValueError: math domain error
,对于复杂的 x
,它将提高 TypeError: can't convert complex to float
。相反,您可以使用 cmath.sqrt()
,它比取幂更准确(并且可能也更快):
>>> import cmath
>>> cmath.sqrt(-25)
5j
>>> cmath.sqrt(8j)
(2+2j)
精度
这两个选项都涉及到 float
的隐式转换,因此 。例如:
>>> 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
非常大的数字甚至可能放不下一个浮点数,您会得到 OverflowError: int too large to convert to float
。参见 Python sqrt limit for very large numbers?
其他类型
我们以Decimal
为例:
求幂失败,除非指数也是 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')
与此同时,math
和 cmath
会默默地将它们的参数分别转换为 float
和 complex
,这可能意味着精度损失。
decimal
也有自己的.sqrt()
. See also calculating n-th roots using Python 3's decimal module
NumPy
>>> import numpy as np
>>> np.sqrt(25)
5.0
>>> np.sqrt([2, 3, 4])
array([1.41421356, 1.73205081, 2. ])
否定
对于负实数,它将 return nan
,因此 np.lib.scimath.sqrt()
可用于这种情况。
>>> 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])
当然,另一种选择是先转换为复数:
>>> a = a.astype(complex)
>>> np.sqrt(a)
array([ 2.+0.j, 0.+1.j, inf+0.j])
SymPy
根据您的目标,尽可能延迟平方根的计算可能是个好主意。 SymPy 可能会有帮助。
SymPy is a Python library for symbolic mathematics.
import sympy
sympy.sqrt(2)
# => sqrt(2)
乍一看这似乎没什么用。
但是 sympy 可以提供比浮点数或小数更多的信息:
sympy.sqrt(8) / sympy.sqrt(27)
# => 2*sqrt(6)/9
此外,没有精度损失。 (√2)² 仍然是一个整数:
s = sympy.sqrt(2)
s**2
# => 2
type(s**2)
#=> <class 'sympy.core.numbers.Integer'>
相比之下,浮点数和小数将 return 一个非常接近 2 但不等于 2 的数字:
(2**0.5)**2
# => 2.0000000000000004
from decimal import Decimal
(Decimal('2')**Decimal('0.5'))**Decimal('2')
# => Decimal('1.999999999999999999999999999')
Sympy 还可以理解更复杂的示例,例如 the Gaussian integral:
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
最后,如果需要十进制表示,可以要求比以往任何时候都需要的更多的数字:
sympy.N(sympy.sqrt(2), 1_000_000)
# => 1.4142135623730950488016...........2044193016904841204
二分查找
免责声明:这是针对更专业的 use-case。此方法可能并非在所有情况下都实用。
好处:
- 可以找到整数值(即哪个 整数 是根?)
- 无需转换为浮点数,精度更高(也可以做到)
我个人为加密 CTF 挑战(RSA 立方根攻击)实现了这个,我需要一个精确的整数值。
大意可以推广到任何其他根。
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
编辑:
正如@wjandrea 也指出的那样,**此示例代码无法计算**。这是一个 side-effect 事实,它不会将任何东西转换为浮点数,因此不会丢失精度。如果根是一个整数,你会得到它。如果不是,您将得到最大的数字,其平方小于您的数字。我更新了代码,以便它也 returns 一个 bool 指示值是否正确,并且还修复了导致它无限循环的问题(@wjandrea 也指出)。这种通用方法的实现对于较小的数字仍然有点奇怪,但在 10 以上时我没有问题。
克服此 method/implementation 的问题和限制:
对于较小的数字,您可以使用其他答案中的所有其他方法。他们通常使用浮点数,这 可能 会损失精度,但对于小整数应该没有问题。所有这些使用浮点数的方法都具有与此相同(或几乎相同)的限制。
如果您仍想使用此方法并获得浮点数结果,将其转换为使用浮点数应该也很简单。请注意,这将重新引入精度损失,这是此方法相对于其他方法的独特优势,在这种情况下,您也可以使用任何其他答案。我觉得牛顿法版本收敛的快一点,但我不确定。
对于较大的数字,浮点数会导致精度损失,此方法可以给出更接近实际答案的结果(取决于输入有多大)。如果您想在此范围内使用 non-integers,您也可以使用其他类型,例如此方法中的固定精度数字。
编辑 2,关于其他答案:
目前,afaik,唯一与此实现具有相似或更好的大数字精度的其他答案是 Eric Duminil 提出的 SymPy。该版本也更易于使用,适用于任何类型的数字,唯一的缺点是它需要 SymPy。如果您正在寻找的话,我的实现没有任何巨大的依赖性。
Python的fractions
模块及其class、Fraction
,实现有理数算术。 Fraction
class 没有实现平方根运算,因为大多数平方根都是无理数。但是,它可用于以任意精度逼近平方根,因为 Fraction
的分子和分母是 arbitrary-precision 整数。
下面的方法取正数x
和迭代次数,returns上下界为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)
有关此操作实施的详细信息,请参阅下面的参考资料。它还展示了如何使用上限和下限实现其他操作(尽管那里的 log
操作显然至少有一个错误)。
- Daumas, M., Lester, D., Muñoz, C.,“验证实数计算:区间算术库”,arXiv:0708.3721 [cs.MS],2007 年。
牛顿法
计算平方根最简单准确的方法是牛顿法。
你有一个数字,你想计算它的平方根 (num
),你猜它的平方根 (estimate
)。估计可以是任何大于 0 的数字,但有意义的数字会显着缩短递归调用深度。
new_estimate = (estimate + num/estimate) / 2
此行使用这 2 个参数计算出更准确的估计值。您可以将 new_estimate
值传递给函数并计算另一个 new_estimate
,它比前一个更准确,或者您可以像这样进行递归函数定义。
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)
例如,我们需要找到 30 的平方根。我们知道结果在5到6之间
newtons_method(30,5)
人数是30,估计是5。每次递归调用的结果是:
5.5
5.477272727272727
5.4772255752546215
5.477225575051661
最后的结果是最准确的数字平方根计算。它与 built-in 函数 math.sqrt()
.
的值相同
这个答案是 originally posted by gunesevitan,但现在被删除了。
任意精度平方根
此变体使用字符串操作将表示十进制 floating-point 数字的字符串转换为 int
,调用 math.isqrt
进行实际的平方根提取,然后格式化结果为十进制字符串。 math.isqrt
向下舍入,因此所有生成的数字都是正确的。
输入字符串 num
必须使用普通浮点格式:不支持 'e' 表示法。 num
字符串可以是普通整数,忽略前导零。
digits
参数指定结果字符串中的小数位数,即小数点后的位数。
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
对于少量数字,使用 decimal.Decimal.sqrt
速度更快。大约 32 位数字左右,str_sqrt
与 Decimal.sqrt
的速度大致相同。但是在 128 位时,str_sqrt
比 Decimal.sqrt
快 2.2 倍,在 512 位时,它快 4.3 倍,在 8192 位时,它快 7.4 倍。
这是 SageMathCell 服务器上的 live version 运行。
# find square-root of a number
while True:
num = int(input("Enter a number:\n>>"))
for i in range(2, num):
cond = (num % i == 0)
if(cond == True):
# print(I)
k = (i*i == num)
if(k == True):
print("Square root of", num, "==>", i)
break
else:
kd = (num**0.5) # (num**(1/2))
print("Square root of", num, "==>", kd)
输出:-
Enter a number: 24
Square root of 24 ==> 4.898979485566356
Enter a number: 36
Square root of 36 ==> 6
Enter a number: 49
Square root of 49 ==> 7
我需要计算一些数字的平方根,例如√9 = 3
和√2 = 1.4142
。我怎样才能在 Python 中做到这一点?
输入可能都是正整数,并且相对较小(比如小于十亿),但如果不是,是否有任何可能会损坏的东西?
相关
- Integer square root in python
- How to find integer nth roots?
- Is there a short-hand for nth root of x in Python?
- Python sqrt limit for very large numbers?
- Which is faster in Python: x**.5 or math.sqrt(x)?
- Why does Python give the "wrong" answer for square root?(具体到Python2)
- calculating n-th roots using Python 3's decimal module
- How can I take the square root of -1 using python?(专注于 NumPy)
- Arbitrary precision of square roots
注意:这是对canonical question after a discussion on Meta about an existing question with the same title.[的尝试=25=]
选项 1:math.sqrt()
标准库中的 math
模块将 a sqrt
function to calculate the square root of a number. It takes any type that can be converted to float
(包括 int
)作为参数,并且 return 是一个 float
.
>>> import math
>>> math.sqrt(9)
3.0
选项 2:分数指数
The power operator (**
) or the built-in pow()
function can also be used to calculate a square root. Mathematically speaking, the square root of a
equals a
to the power of 1/2
.
幂运算符需要数字类型并匹配 the conversion rules for binary arithmetic operators,因此在本例中它将 return 为 float
或 complex
数字。
>>> 9 ** (1/2)
3.0
>>> 9 ** .5 # Same thing
3.0
>>> 2 ** .5
1.4142135623730951
(注意:在 Python 2 中,1/2
被截断为 0
,因此您必须使用 1.0/2
或类似的强制浮点运算。参见 Why does Python give the "wrong" answer for square root?)
此方法可以推广到 nth root,但不能精确表示为 float
的分数(例如 1/3 或任何不是 2 的幂的分母)可能会导致一些不准确的地方:
>>> 8 ** (1/3)
2.0
>>> 125 ** (1/3)
4.999999999999999
边缘情况
消极且复杂
求幂适用于负数和复数,但结果略有不准确:
>>> (-25) ** .5 # Should be 5j
(3.061616997868383e-16+5j)
>>> 8j ** .5 # Should be 2+2j
(2.0000000000000004+2j)
注意 -25
上的括号!否则它被解析为 -(25**.5)
因为 exponentiation is more tightly binding than unary negation.
与此同时,math
仅适用于浮点数,因此对于 x<0
,math.sqrt()
将提高 ValueError: math domain error
,对于复杂的 x
,它将提高 TypeError: can't convert complex to float
。相反,您可以使用 cmath.sqrt()
,它比取幂更准确(并且可能也更快):
>>> import cmath
>>> cmath.sqrt(-25)
5j
>>> cmath.sqrt(8j)
(2+2j)
精度
这两个选项都涉及到 float
的隐式转换,因此
>>> 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
非常大的数字甚至可能放不下一个浮点数,您会得到 OverflowError: int too large to convert to float
。参见 Python sqrt limit for very large numbers?
其他类型
我们以Decimal
为例:
求幂失败,除非指数也是 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')
与此同时,math
和 cmath
会默默地将它们的参数分别转换为 float
和 complex
,这可能意味着精度损失。
decimal
也有自己的.sqrt()
. See also calculating n-th roots using Python 3's decimal module
NumPy
>>> import numpy as np
>>> np.sqrt(25)
5.0
>>> np.sqrt([2, 3, 4])
array([1.41421356, 1.73205081, 2. ])
否定
对于负实数,它将 return nan
,因此 np.lib.scimath.sqrt()
可用于这种情况。
>>> 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])
当然,另一种选择是先转换为复数:
>>> a = a.astype(complex)
>>> np.sqrt(a)
array([ 2.+0.j, 0.+1.j, inf+0.j])
SymPy
根据您的目标,尽可能延迟平方根的计算可能是个好主意。 SymPy 可能会有帮助。
SymPy is a Python library for symbolic mathematics.
import sympy
sympy.sqrt(2)
# => sqrt(2)
乍一看这似乎没什么用。
但是 sympy 可以提供比浮点数或小数更多的信息:
sympy.sqrt(8) / sympy.sqrt(27)
# => 2*sqrt(6)/9
此外,没有精度损失。 (√2)² 仍然是一个整数:
s = sympy.sqrt(2)
s**2
# => 2
type(s**2)
#=> <class 'sympy.core.numbers.Integer'>
相比之下,浮点数和小数将 return 一个非常接近 2 但不等于 2 的数字:
(2**0.5)**2
# => 2.0000000000000004
from decimal import Decimal
(Decimal('2')**Decimal('0.5'))**Decimal('2')
# => Decimal('1.999999999999999999999999999')
Sympy 还可以理解更复杂的示例,例如 the Gaussian integral:
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
最后,如果需要十进制表示,可以要求比以往任何时候都需要的更多的数字:
sympy.N(sympy.sqrt(2), 1_000_000)
# => 1.4142135623730950488016...........2044193016904841204
二分查找
免责声明:这是针对更专业的 use-case。此方法可能并非在所有情况下都实用。
好处:
- 可以找到整数值(即哪个 整数 是根?)
- 无需转换为浮点数,精度更高(也可以做到)
我个人为加密 CTF 挑战(RSA 立方根攻击)实现了这个,我需要一个精确的整数值。
大意可以推广到任何其他根。
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
编辑:
正如@wjandrea 也指出的那样,**此示例代码无法计算**。这是一个 side-effect 事实,它不会将任何东西转换为浮点数,因此不会丢失精度。如果根是一个整数,你会得到它。如果不是,您将得到最大的数字,其平方小于您的数字。我更新了代码,以便它也 returns 一个 bool 指示值是否正确,并且还修复了导致它无限循环的问题(@wjandrea 也指出)。这种通用方法的实现对于较小的数字仍然有点奇怪,但在 10 以上时我没有问题。
克服此 method/implementation 的问题和限制:
对于较小的数字,您可以使用其他答案中的所有其他方法。他们通常使用浮点数,这 可能 会损失精度,但对于小整数应该没有问题。所有这些使用浮点数的方法都具有与此相同(或几乎相同)的限制。
如果您仍想使用此方法并获得浮点数结果,将其转换为使用浮点数应该也很简单。请注意,这将重新引入精度损失,这是此方法相对于其他方法的独特优势,在这种情况下,您也可以使用任何其他答案。我觉得牛顿法版本收敛的快一点,但我不确定。
对于较大的数字,浮点数会导致精度损失,此方法可以给出更接近实际答案的结果(取决于输入有多大)。如果您想在此范围内使用 non-integers,您也可以使用其他类型,例如此方法中的固定精度数字。
编辑 2,关于其他答案:
目前,afaik,唯一与此实现具有相似或更好的大数字精度的其他答案是 Eric Duminil 提出的 SymPy。该版本也更易于使用,适用于任何类型的数字,唯一的缺点是它需要 SymPy。如果您正在寻找的话,我的实现没有任何巨大的依赖性。
Python的fractions
模块及其class、Fraction
,实现有理数算术。 Fraction
class 没有实现平方根运算,因为大多数平方根都是无理数。但是,它可用于以任意精度逼近平方根,因为 Fraction
的分子和分母是 arbitrary-precision 整数。
下面的方法取正数x
和迭代次数,returns上下界为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)
有关此操作实施的详细信息,请参阅下面的参考资料。它还展示了如何使用上限和下限实现其他操作(尽管那里的 log
操作显然至少有一个错误)。
- Daumas, M., Lester, D., Muñoz, C.,“验证实数计算:区间算术库”,arXiv:0708.3721 [cs.MS],2007 年。
牛顿法
计算平方根最简单准确的方法是牛顿法。
你有一个数字,你想计算它的平方根 (num
),你猜它的平方根 (estimate
)。估计可以是任何大于 0 的数字,但有意义的数字会显着缩短递归调用深度。
new_estimate = (estimate + num/estimate) / 2
此行使用这 2 个参数计算出更准确的估计值。您可以将 new_estimate
值传递给函数并计算另一个 new_estimate
,它比前一个更准确,或者您可以像这样进行递归函数定义。
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)
例如,我们需要找到 30 的平方根。我们知道结果在5到6之间
newtons_method(30,5)
人数是30,估计是5。每次递归调用的结果是:
5.5
5.477272727272727
5.4772255752546215
5.477225575051661
最后的结果是最准确的数字平方根计算。它与 built-in 函数 math.sqrt()
.
这个答案是 originally posted by gunesevitan,但现在被删除了。
任意精度平方根
此变体使用字符串操作将表示十进制 floating-point 数字的字符串转换为 int
,调用 math.isqrt
进行实际的平方根提取,然后格式化结果为十进制字符串。 math.isqrt
向下舍入,因此所有生成的数字都是正确的。
输入字符串 num
必须使用普通浮点格式:不支持 'e' 表示法。 num
字符串可以是普通整数,忽略前导零。
digits
参数指定结果字符串中的小数位数,即小数点后的位数。
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
对于少量数字,使用 decimal.Decimal.sqrt
速度更快。大约 32 位数字左右,str_sqrt
与 Decimal.sqrt
的速度大致相同。但是在 128 位时,str_sqrt
比 Decimal.sqrt
快 2.2 倍,在 512 位时,它快 4.3 倍,在 8192 位时,它快 7.4 倍。
这是 SageMathCell 服务器上的 live version 运行。
# find square-root of a number
while True:
num = int(input("Enter a number:\n>>"))
for i in range(2, num):
cond = (num % i == 0)
if(cond == True):
# print(I)
k = (i*i == num)
if(k == True):
print("Square root of", num, "==>", i)
break
else:
kd = (num**0.5) # (num**(1/2))
print("Square root of", num, "==>", kd)
输出:-
Enter a number: 24
Square root of 24 ==> 4.898979485566356
Enter a number: 36
Square root of 36 ==> 6
Enter a number: 49
Square root of 49 ==> 7