【问题标题】:How to perform square root without using math module?如何在不使用数学模块的情况下执行平方根?
【发布时间】:2010-06-15 16:19:01
【问题描述】:

我想在不使用数学模块的情况下找到数字的平方根,因为我需要调用该函数大约 20k 次,并且不想通过每次调用函数时链接到数学模块来减慢执行速度

有没有更快更简单的求平方根的方法?

【问题讨论】:

  • sqrt() 很慢。您真的需要找到 20k 个不同的根,还是可以使用查找表或缓存结果?

标签: python


【解决方案1】:

导入数学模块只发生一次,您可能不会比数学模块快多少。还有一个关于 Which is faster in Python: x**.5 or math.sqrt(x)? 的旧 Stackoverflow 问题。目前尚不清楚哪种方法更快。

也许看看 NumPySciPy,不一定是 sqrt,但如果你做一些繁重的计算,它们可能会很方便。

【讨论】:

  • +1 用于回答提出的问题和实际需要回答的问题!
  • 您知道,在我的测试中,使用 Numpy 数组并将整个数组提升到 0.5 次幂,在迭代同一个 numpy 数组的测试中,20k 平方根的速度至少提高了 60 倍.
  • 在 Mac OS X 上使用 Python 2.6.5,sqrt() 实际上更快(见我的回答)。这两种方法实际上是不同的,哪一种更快取决于实现细节。
  • 我已经删除了关于哪个更快的文本。
【解决方案2】:

正如 Fabian 所说,很难比math.sqrt 更快。原因是它从C库中调用了对应的函数,用CPython。

但是,您可以通过消除属性查找的开销来加快速度:

from math import sqrt

随后对 sqrt 的每次调用不必在数学模块中查找它,这样可以节省执行时间:

print sqrt(2)

这里是计时数字,从最快到最慢(Python 2.6.5、Mac OS X 10.6.3):sqrt**0.5 快:

lebigot@weinberg ~ % python -m timeit -s 'from math import sqrt; x = 2' 'sqrt(x)'
1000000 loops, best of 3: 0.207 usec per loop
lebigot@weinberg ~ % python -m timeit -s 'x = 2' 'x**0.5'
1000000 loops, best of 3: 0.226 usec per loop
lebigot@weinberg ~ % python -m timeit -s 'import math; x = 2' 'math.sqrt(x)'
1000000 loops, best of 3: 0.268 usec per loop

请注意,时序测试会计算变量的平方根。他们不计算像2**0.5 这样的常数,因为2**0.5 在CPython 中是预先计算的:

import dis

def f():
    return 2**0.5

print dis.dis(f)

打印

2           0 LOAD_CONST               3 (1.4142135623730951)
            3 RETURN_VALUE        

你在哪里看到常量 float sqrt(2) = 1.414…

如果您操作数字数组,NumPy 的 sqrt 是要走的路,正如另一个答案中提到的那样。

【讨论】:

  • 它真的在每次迭代中运行代码而不以任何方式缓存它吗?我对它的性能感到非常惊讶,我尝试了 10 (8.04 ns)、10000 (8.18 ns)、10000000 (8.31 ns)、10000000000 (8.23 ns),这对我来说似乎是恒定的时间。现在我不确定这种方式,我错过了什么吗?它真的(接近)恒定时间吗?
  • 我猜你列出了许多迭代?你的具体测试代码是什么?
  • 这里是python -m timeit -s 'import math; x= 10000000000; math.sqrt(x)'
  • 用于计算平方根的算法可能确实在恒定时间内运行,因此您观察到的计时结果不会让我感到震惊 这例如可能用于 32 位浮点数 (en.m.wikipedia.org/wiki/Fast_inverse_square_root) .
【解决方案3】:

我认为数学库可能与您自己编写的任何东西一样快。但如果你想自己写,这里有一种算法。我不懂Python,就写一些伪代码吧。

function sqrt(x)
  lastGuess=x/2
  loop
    guess=(lastGuess+x/lastGuess)/2
    if abs(guess-lastGuess)<.000001 // or whatever threshold you want
      exit loop
    lastGuess=guess
  return guess

以及翻译成 Python 的伪代码:

def sqrt(x):
    last_guess= x/2.0
    while True:
        guess= (last_guess + x/last_guess)/2
        if abs(guess - last_guess) < .000001: # example threshold
            return guess
        last_guess= guess

【讨论】:

【解决方案4】:

你应该在 Jupyter Notebook 中输入这一行:

25**(1/2)

【讨论】:

    【解决方案5】:

    在某些特殊情况下,您可以用程序大小来换取极快的速度。创建一个大数组并存储每个平方根运算的预计算结果(使用输入值作为索引)。它非常有限,但你不会得到更快的东西。

    (地震就是这样弄的)

    【讨论】:

    • 如果值都是小整数,这可以工作。如果这些值是任意实数,这将不起作用。此外,这仅在您希望看到相同的数字多次出现时才有效。即使这样,我也不会计算可能永远不会出现在输入中的数千个数字的平方根,而是构建一个缓存并在第一次需要时计算平方根,然后使用缓存中的值。
    【解决方案6】:

    使用幂运算符,将您的数字提高到 1/2 次方:

    >>> 2**0.5
    1.4142135623730951
    

    至于是否更快:

    >>> timeit.timeit(stmt='sqrt(x)', setup='from math import sqrt; x = 2')
    0.7182440785071833
    >>> timeit.timeit(stmt='x**0.5', setup='from math import sqrt; x = 2')
    0.87514279049432275
    

    【讨论】:

    • 我在我的计算机上得到了类似的结果,但是当我从我链接到的问题的接受答案中尝试基准测试时,math.sqrt 更快。这里发生了一些有趣的事情。
    • 这些时序测试不是很有代表性:你可以看我的回答,这表明'**0.5'实际上比math.sqrt慢。原因是2**0.5是一个预先计算好的数值常数。
    • @EOL - 我得到了与其他数字类似的结果(即,0.42521**0.5 大约比 sqrt(0.42521) 快 7 倍)。我没有得出任何结论,但测试对我来说似乎是有效的。
    • 您确实会得到相同的结果,因为0.42521**0.5 是由 Python 预编译的(任何 0.5 次幂的常数也是如此):当您运行代码时,no 数学执行操作;相反,Python 只加载一个常数(请参阅我的答案中的反汇编代码)。如果计算时间很重要,那么只有在计算 变量 的平方根时(不是常数);因此,实际的测试应该包括取一个变量的平方根。
    • 我很密集,我现在明白了。谢谢 :P 修复了测试。
    【解决方案7】:

    您可以实现 Newton 的方法,但是虽然它非常快,但它不太可能比我假设在数学模块中实现的 C 版本更快。见http://en.wikipedia.org/wiki/Methods_of_computing_square_roots

    【讨论】:

      【解决方案8】:

      平方根函数

       def sqrt(number):
          if number ** 0.5 == int(number ** 0.5):
            return True
          else:
            return False
      

      【讨论】:

        【解决方案9】:

        有点晚了,但无论如何我想提一下这些简单的算术:

        25**(1/2)
        

        pow(25, 0,5)
        

        将返回 5. 享受。

        【讨论】:

          【解决方案10】:

          用于计算平方的 Python 代码 sn-p。它首先进行初始猜测,如果猜测不够好,它会迭代,直到我们有一个好的猜测

          def gen_square_root_v1(number, epsilon):
          
             #boundary condition check
          
             if number == '1':
                return 1
          
             elif number <= 0:
                print('this computes square root for positive numbers only' )
          
             else:
                pass
          
          
             prev_estimate = number/2
          
             while True: 
          
                 #each itearation, calculate a new estimate
                 new_estimate = (prev_estimate + number/prev_estimate)/2
          
                 #Alternatively can use if abs(new_estimate - prev_estimate) < epsilon:
          
                 #check the difference between  square of new_estimate and number
                 if abs(new_estimate * new_estimate - number) < epsilon:
                   return prev_estimate
          
               #if guess is not good enough, use it to make the next guess           
               prev_estimate = new_estimate
          
          
          #call the function    
          print(gen_square_root_v1(16,1e-5))
          

          【讨论】:

          • 这是什么?这不是 OP 所要求的。
          【解决方案11】:

          下面的代码是在不使用python的内置方法的情况下找到数字的平方根。代码很容易理解,因为我使用数学简单解决方案编写了代码。

           x=float(input())
           min1=0 
           max1=x
           for i in range(10):
               mid=(min1+max1)/2  #middle value
               res=mid**2       #finding the square of middle value
               if res==x:       #if the middle value is square root program break here
                  break        
               elif res>x:      #if the square value of the middle value is more than x then we need to take max value as middle 
                  max1=mid
               else:                  #if the square value of the middle value is less than x then we need to take min  value as middle 
                  min1=mid
          
           print(mid)
            
          

          【讨论】:

            猜你喜欢
            • 1970-01-01
            • 2014-01-23
            • 1970-01-01
            • 2011-03-04
            • 2017-01-29
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 2012-09-07
            相关资源
            最近更新 更多