如何在Python中计算平方根?

wja*_*rea 53 python math sqrt

我需要计算一些数字的平方根,例如\xe2\x88\x9a9 = 3\xe2\x88\x9a2 = 1.4142。我怎样才能用Python做到这一点?

\n

输入可能都是正整数,并且相对较小(比如小于十亿),但万一它们不是,有什么可能会破坏吗?

\n
\n \n

注意:这是在 Meta 上讨论现有同标题问题后对规范问题的尝试。

\n

有关的

\n\n
\n

wja*_*rea 87

选项1:math.sqrt()

math标准库中的模块有一个计算sqrt数字平方根的函数。它接受任何可以转换为的float类型(包括int)并返回float

>>> import math
>>> math.sqrt(9)
3.0
Run Code Online (Sandbox Code Playgroud)

选项 2:小数指数

幂运算符 ( **)或内置pow()函数也可用于计算平方根。从数学上来说,的平方根a等于a的次方1/2

幂运算符需要数字类型并匹配二进制算术运算符的转换规则,因此在这种情况下它将返回 afloatcomplex数字。

>>> 9 ** (1/2)
3.0
>>> 9 ** .5  # Same thing
3.0
>>> 2 ** .5
1.4142135623730951
Run Code Online (Sandbox Code Playgroud)

(注意:在 Python 2 中,1/2被截断为0,因此您必须强制使用1.0/2或 类似的浮点算术。请参阅Why does Python Give the “wrong” answer for square root?

此方法可以推广到nth root,尽管无法精确表示为 a 的分数float(例如 1/3 或任何不是 2 的幂的分母)可能会导致一些不准确:

>>> 8 ** (1/3)
2.0
>>> 125 ** (1/3)
4.999999999999999
Run Code Online (Sandbox Code Playgroud)

边缘情况

消极且复杂

求幂适用于负数和复数,但结果有一些轻微的不准确:

>>> (-25) ** .5  # Should be 5j
(3.061616997868383e-16+5j)
>>> 8j ** .5  # Should be 2+2j
(2.0000000000000004+2j)
Run Code Online (Sandbox Code Playgroud)

(注意: 上需要括号-25,否则它会被解析为-(25**.5)因为求幂比求反具有更紧密的绑定。)

同时,math仅针对浮点数构建,因此对于x<0math.sqrt(x)将引发ValueError: math domain error,对于复杂x,它将引发TypeError: can't convert complex to float。相反,您可以使用cmath.sqrt(x),它比求幂更准确(并且可能也更快):

>>> import cmath
>>> cmath.sqrt(-25)
5j
>>> cmath.sqrt(8j)
(2+2j)
Run Code Online (Sandbox Code Playgroud)

精确

这两个选项都涉及隐式转换float,因此浮点精度是一个因素。例如,让我们尝试一个大数字:

>>> n = 10**30
>>> x = n**2
>>> root = x**.5
>>> root == n
False
>>> root - n  # how far off are they?
0.0
>>> int(root) - n  # how far off is the float from the int?
19884624838656
Run Code Online (Sandbox Code Playgroud)

非常大的数字甚至可能无法放入浮点数中,您会得到OverflowError: int too large to convert to float. 看到Python sqrt限制非常大的数字?

其他类型

让我们看一下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')
Run Code Online (Sandbox Code Playgroud)

同时,mathcmath会默默地将它们的参数分别转换为floatcomplex,这可能意味着精度损失。

decimal也有自己的.sqrt()。另请参阅使用 Python 3 的十进制模块计算 n 次根

  • @wjandrea:“为什么 cmath 使用不同的算法?” &lt;- 与什么不同?您是否在问为什么“cmath.sqrt(z)”不只使用“z ** 0.5”?如果是这样,答案是一般复数幂是比平方根更复杂的算法(采用复数对数、缩放,然后采用结果的复数指数),有更多机会损失准确性,因此 `cmath.sqrt(z )` 可能比 `z ** 0.5` 更快并且更准确。我的建议是始终使用显式的“sqrt”函数或方法,而不是一般的供电操作。 (12认同)
  • “求幂适用于负数和复数,尽管结果略有偏差,我不确定为什么:”这是因为复数运算/结果和复数都不是数轴(从 -inf 到 + inf) 而是一个 2D 平面(也是 -inf j 和 +inf j)。比较 √x=1 的解 +1 * 和 -1* - 即数轴的两个“方向”。由于复数代表一个平面,因此 sqrt 结果是复平面上的一个圆。在该圆上选取*一个*结果在数值上不稳定,因此某些算法会产生不准确的结果。 (3认同)

Eri*_*nil 24

符号

\n

根据您的目标,尽可能延迟平方根的计算可能是个好主意。SymPy可能会有所帮助。

\n
\n

SymPy 是一个用于符号数学的 Python 库。

\n
\n
import sympy\nsympy.sqrt(2)\n# => sqrt(2)\n
Run Code Online (Sandbox Code Playgroud)\n

乍一看这似乎不太有用。

\n

但 sympy 可以提供比浮点数或小数更多的信息:

\n
sympy.sqrt(8) / sympy.sqrt(27)\n# => 2*sqrt(6)/9\n
Run Code Online (Sandbox Code Playgroud)\n

而且,精度不会丢失。(\xe2\x88\x9a2)\xc2\xb2 仍然是一个整数:

\n
s = sympy.sqrt(2)\ns**2\n# => 2\ntype(s**2)\n#=> <class \'sympy.core.numbers.Integer\'>\n
Run Code Online (Sandbox Code Playgroud)\n

相比之下,浮点数和小数将返回一个非常接近 2 但不等于 2 的数字:

\n
(2**0.5)**2\n# => 2.0000000000000004\n\nfrom decimal import Decimal\n(Decimal(\'2\')**Decimal(\'0.5\'))**Decimal(\'2\')\n# => Decimal(\'1.999999999999999999999999999\')\n
Run Code Online (Sandbox Code Playgroud)\n

Sympy 还可以理解更复杂的示例,例如高斯积分

\n
from sympy import Symbol, integrate, pi, sqrt, exp, oo\nx = Symbol(\'x\')\nintegrate(exp(-x**2), (x, -oo, oo))\n# => sqrt(pi)\nintegrate(exp(-x**2), (x, -oo, oo)) == sqrt(pi)\n# => True\n
Run Code Online (Sandbox Code Playgroud)\n

最后,如果需要十进制表示,则可以要求比实际需要更多的数字:

\n
sympy.N(sympy.sqrt(2), 1_000_000)\n# => 1.4142135623730950488016...........2044193016904841204\n
Run Code Online (Sandbox Code Playgroud)\n


wja*_*rea 15

数值模拟

>>> import numpy as np
>>> np.sqrt(25)
5.0
>>> np.sqrt([2, 3, 4])
array([1.41421356, 1.73205081, 2.        ])
Run Code Online (Sandbox Code Playgroud)

文档

消极的

对于负实数,它将返回nan,因此np.emath.sqrt()适用于这种情况。

>>> a = np.array([4, -1, np.inf])
>>> np.sqrt(a)
<stdin>:1: RuntimeWarning: invalid value encountered in sqrt
array([ 2., nan, inf])
>>> np.emath.sqrt(a)
array([ 2.+0.j,  0.+1.j, inf+0.j])
Run Code Online (Sandbox Code Playgroud)

当然,另一种选择是先转换为复数:

>>> a = a.astype(complex)
>>> np.sqrt(a)
array([ 2.+0.j,  0.+1.j, inf+0.j])
Run Code Online (Sandbox Code Playgroud)