在python中仅使用整数数学计算平方根

时间:2016-07-04 04:33:45

标签: python math square-root

我正在开发一种不支持浮点数学的微控制器。仅限整数数学。因此,没有sqrt()函数,我无法导入任何数学模块。 MCU正在运行python的一个子集,它支持八种Python数据类型:None,integer,Boolean,string,function,tuple,byte list和iterator。此外,MCU无法进行分区(//)。

我的问题是我需要计算3个有符号整数的大小。

mag = sqrt(x**2+y**2+z**2)

FWIW,这些值只能在+/- 1024的范围内,我只需要近似。有没有人有解决这个问题的模式?

2 个答案:

答案 0 :(得分:3)

请注意,最大可能总和为3 * 1024 ** 2,因此最大可能的平方根为1773(底部 - 或1774舍入)。

所以你可以简单地将0作为开始猜测,并重复加1直到该平方超过总和。这不可能超过1770次迭代。

当然,这可能太慢了。直接的二进制搜索可以将其减少到11次迭代,并且不需要除法(假设MCU可以向右移1位,这与2次除法相同)。

修改

这里有一些代码,用于返回真正平方根的底层的二进制搜索:

def isqrt(n):
    if n <= 1:
        return n
    lo = 0
    hi = n >> 1
    while lo <= hi:
        mid = (lo + hi) >> 1
        sq = mid * mid
        if sq == n:
            return mid
        elif sq < n:
            lo = mid + 1
            result = mid
        else:
            hi = mid - 1
    return result

要检查,请运行:

from math import sqrt
assert all(isqrt(i) == int(sqrt(i)) for i in range(3*1024**2 + 1))

根据你的说法检查所有可能的输入 - 并且由于二进制搜索在所有情况下都是正确的,所以检查每个案例都很好!它并不需要很长时间才能实现真正的&#34;机器; - )

可能重要

为防止可能出现溢出并显着加快速度,请将lohi的初始化更改为:

    hi = 1
    while hi * hi <= n:
        hi <<= 1
    lo = hi >> 1

然后运行时与结果中的位数成比例,大大加快了较小的结果。事实上,对于&#34;关闭&#34;的草率足够的定义,你可以在那里停下来。

FOR POSTERITY; - )

看起来OP根本不需要平方根。但对于那些可能并且无法承担分工的人来说,这里是代码的简化版本,也可以从初始化中删除乘法。注意:我没有使用.bit_length(),因为许多部署的Python版本都不支持。

def isqrt(n):
    if n <= 1:
        return n
    hi, hisq = 2, 4
    while hisq <= n:
        hi <<= 1
        hisq <<= 2
    lo = hi >> 1
    while hi - lo > 1:
        mid = (lo + hi) >> 1
        if mid * mid <= n:
            lo = mid
        else:
            hi = mid
    assert lo + 1 == hi
    assert lo**2 <= n < hi**2
    return lo

from math import sqrt
assert all(isqrt(i) == int(sqrt(i)) for i in range(3*1024**2 + 1))

答案 1 :(得分:1)

有一个计算它的算法,但是它使用了分区,没有这就是我想到的那个

def isqrt_linel(n):
    x = 0
    while (x+1)**2 <= n:
        x+=1
    return x

顺便说一下,我所知道的算法使用牛顿法:

def isqrt(n):
    #https://en.wikipedia.org/wiki/Integer_square_root
    #https://gist.github.com/bnlucas/5879594
    if n>=0:
        if n == 0:
            return 0
        a, b = divmod(n.bit_length(), 2)
        x = 2 ** (a + b)
        while True:
            y = (x + n // x) >> 1
            if y >= x:
                return x
            x = y
    else:
        raise ValueError("negative number")