代码之家  ›  专栏  ›  技术社区  ›  wjandrea Geographos

如何在Python中计算平方根?

  •  2
  • wjandrea Geographos  · 技术社区  · 2 年前
    1 回复  |  直到 2 年前
        1
  •  74
  •   wjandrea    2 年前

    选项1: math.sqrt()

    这个 math 标准库中的模块具有 a sqrt function 计算一个数字的平方根。它采用任何类型 can be converted to float (包括 int )作为参数,并返回 浮动

    >>> import math
    >>> math.sqrt(9)
    3.0
    

    选项2:分数指数

    The power operator ( ** ) 或内置 pow() 函数也可以用来计算平方根。从数学上讲, the square root of a equals a to the power of 1/2 .

    幂运算符需要数字类型和匹配项 the conversion rules for binary arithmetic operators ,因此在这种情况下,它将返回 浮动 或者 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 ,尽管分数不能准确地表示为 浮动 (比如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

    同时 数学 只为浮子而建,因此 x<0 , math.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)
    

    精确

    这两个选项都涉及到的隐式转换 浮动 所以 floating point precision is a factor 。例如:

    >>> n = 10**30
    >>> x = n**2
    >>> root = x**.5
    >>> n == root
    False
    >>> n - root  # how far off are they?
    0.0
    >>> int(root) - 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('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')
    

    同时 数学 cmath 将静默地将其参数转换为 浮动 复杂的 这可能意味着精度的损失。

    decimal 也有自己的 .sqrt() 。另请参阅 calculating n-th roots using Python 3's decimal module

        2
  •  22
  •   wjandrea Geographos    2 年前

    SymPy

    根据你的目标,尽可能长时间地推迟平方根的计算可能是个好主意。 SymPy 可能会有所帮助。

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

    import sympy
    sympy.sqrt(2)
    # => sqrt(2)
    

    一开始这似乎不是很有用。

    但sympy可以提供比float或Decimals更多的信息:

    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'>
    

    相比之下,浮点和小数将返回一个非常接近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
    
        3
  •  15
  •   wjandrea    2 年前

    NumPy

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

    docs

    消极的

    对于负实数,它将返回 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])
    

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

    >>> a = a.astype(complex)
    >>> np.sqrt(a)
    array([ 2.+0.j,  0.+1.j, inf+0.j])
    
        4
  •  4
  •   wjandrea    2 年前

    牛顿法

    计算平方根最简单、最准确的方法是牛顿法。

    你有一个数字,你想计算它的平方根( num )你可以猜测它的平方根( estimate )。估计可以是大于0的任何数字,但有意义的数字会显著缩短递归调用深度。

    new_estimate = (estimate + num/estimate) / 2
    

    这条线使用这两个参数来计算更准确的估计。你可以通过 new_estimate 值,然后计算另一个 新估计(_E) 这比上一个更准确,或者你可以像这样做一个递归函数定义。

    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
    

    最后一个结果是数字平方根的最精确计算。它与内置函数的值相同 math.sqrt()


    这个答案是 originally posted 通过 gunesevitan ,但现在已被删除。

        5
  •  4
  •   Peter O. epatel    2 年前

    Python的 fractions 模块及其类, Fraction ,用有理数实现算术。这个 小部分 类不实现平方根运算,因为大多数平方根都是无理数。然而,它可以用于以任意精度近似平方根,因为 小部分 的分子和分母是任意精度的整数。

    以下方法取正数 x 和多次迭代,并返回的平方根的上下限 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。

    或者,使用Python的 math.isqrt ,我们可以计算任意精度的平方根:

    • 的平方根 i 1/2以内 n 的正确值,其中 是一个整数: Fraction(math.isqrt(i * 2**(n*2)), 2**n)
    • 的平方根 1/10以内 n 的正确值,其中 是一个整数: Fraction(math.isqrt(i * 10**(n*2)), 10**n)
    • 的平方根 x 1/2以内 n 的正确值,其中 x 是1/2的倍数 n : Fraction(math.isqrt(x * 2**(n)), 2**n)
    • 的平方根 x 1/10以内 n 的正确值,其中 x 是1/10的倍数 n : Fraction(math.isqrt(x * 10**(n)), 10**n)

    在前述内容中, x 必须为0或更大。

        6
  •  3
  •   Uncle Dino    2 年前

    二进制搜索

    免责声明: 这是一个更专业的用例。这种方法可能不适用于所有情况。

    优点:

    • 可以找到整数值(即 整数 是根吗?)
    • 无需转换为浮点,因此精度更高(也可以做得很好)

    我个人为一个加密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也指出的,**这个示例代码不能计算**。这是一个副作用,因为它不会将任何内容转换为浮点值,所以不会丢失精度。如果根是一个整数,你会得到它。如果不是,你会得到一个最大的数,它的平方比你的数小。我更新了代码,这样它也会返回一个bool,指示值是否正确,还修复了导致它无限循环的问题(@wjandrea也指出了这一点)。这种通用方法的实现对于较小的数字仍然有点奇怪,但在10以上我没有问题。

    克服这种方法/实施的问题和局限性:

    对于较小的数字,您可以使用其他答案中的所有其他方法。他们通常使用浮子 可以 是精度的损失,但对于小整数来说,这应该意味着根本没有问题。所有使用浮点数的方法都有相同(或几乎相同)的限制。

    如果您仍然想使用此方法并获得浮点结果,那么将其转换为使用浮点也应该很简单。请注意,这将重新引入精度损失,这是该方法相对于其他方法的独特优势,在这种情况下,您也可以只使用任何其他答案。我认为牛顿方法的收敛速度更快,但我不确定。

    对于较大的数字,浮点运算会导致精度损失,这种方法可以给出更接近实际答案的结果(取决于输入的大小)。如果你想处理这个范围内的非整数,你可以使用其他类型,例如这个方法中的固定精度数字。

    编辑2,关于其他答案:

    目前,afaik,唯一一个对大数字具有类似或更好精度的答案是Eric Duminil提出的SymPy。该版本也更容易使用,适用于任何类型的数字,唯一的缺点是它需要SymPy。我的实现没有任何巨大的依赖性,如果这是您想要的。

        7
  •  3
  •   PM 2Ring    2 年前

    任意精度平方根

    此变体使用字符串操作将表示十进制浮点数的字符串转换为 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 比2.2快 小数.sqrt ,512位,快4.3位,8192位,快7.4位。

    这是一个 live version 在SageMathCell服务器上运行。

        8
  •  -3
  •   Mark Dickinson Alexandru    2 年前

    求一个数字的平方根

    while True:
        num = int(input("Enter a number:\n>>"))
        for i in range(2, num):
            if num % i == 0:
                if i*i == num:
                    print("Square root of", num, "==>", i)
                    break
        else:
            kd = (num**0.5)  # (num**(1/2))
            print("Square root of", num, "==>", kd)
    

    输出:-

    输入一个数字:24
    24的平方根==>4.898979485566356
    输入一个数字:36
    36的平方根==>6.
    输入一个数字:49
    49的平方根===>7.

    输出点击下方&请参阅

    Output1

    Output2