为什么这个算法对于不是2的幂的整数会失败?

时间:2012-07-21 20:35:01

标签: python algorithm python-3.x

基于this answer,我在 Python 3.x 中实现了一个简单的算法,以确定整数n是否是另一个整数的幂{{1} 1}}。但是,该算法不会返回正确的结果。链接答案中的代码是:

base

(A comment表示从逻辑上检查while (n % 3 == 0) { n /= 3; } return n == 1; 也是必要的。这是我的代码:

n == 0

我编写了简单的测试代码来测试某些范围的基数和指数,但它返回的结果是不正确的。测试代码:

def is_power(n: int, base: int) -> bool:
    if n == 0:
        return false
    while (n % base == 0):
        n /= base
    return n == 1

我用更大的范围测试了它,但是为了输出,我在这个例子中限制了它。这输出:

for base in range(3, 10):
    print("Base: {0}".format(base))
    for exp in range(30, 40):
        b = is_power(pow(base, exp), base)
        if not(b):
            print("{: <3}: {: <5}".format(exp, str(b)))

这显然是不正确的。我尝试调试一个小例子,其中Base: 3 35 : False 36 : False 37 : False 38 : False 39 : False Base: 4 Base: 5 30 : False 31 : False 32 : False 33 : False 34 : False 35 : False 36 : False 37 : False 38 : False 39 : False Base: 6 35 : False 36 : False 37 : False 38 : False 39 : False Base: 7 30 : False 31 : False 32 : False 33 : False 34 : False 35 : False 36 : False 37 : False 38 : False 39 : False Base: 8 Base: 9 30 : False 31 : False 32 : False 33 : False 34 : False 35 : False 36 : False 37 : False 38 : False 39 : False n = pow(3, 35)在循环中产生base = 3的这些值:

n

并且循环结束,因为50031545098999707/3 == 1.667718169966656 9 e + 16(注意最后一位数字不同)。这是问题吗? Python的计算失败了吗?如果没有,这个算法有什么问题?

如果我使用50031545098999707 1.6677181699666568e+16 代码,算法也会失败,但我不一定会对此感到惊讶,因为查看一个示例表明math.powpow并非总是如此返回相同的值。

math.pow

2 个答案:

答案 0 :(得分:7)

由于您使用的是Python 3,因此您应该使用

def is_power(n: int, base: int) -> bool:
    if n == 0:
        return false
    while (n % base == 0):
        n //= base
    #     ^^ note two slashes
    return n == 1

在Python 3.x中,/操作始终执行&#34;实际划分&#34;并返回一个浮点数,但在你链接的算法中,它期望/进行整数除法,这是用//完成的( &#34;楼层划分&#34;)运营商。

测试失败真的是因为浮点运算缺乏精度,正如您所料。当/返回无限精度实数时,该算法仍然正确。以3.0 34 为例,

3.0 ** 34 == 16677181699666569
          == 0b111011001111111100111011110011000100000011001010001001

默认浮点格式仅支持5 b <3>位的精度,但上面的数字需要5 b <4 b 位来表示,所以我们必须舍入最后一位位(1),导致

             0b111011001111111100111011110011000100000011001010001000
                                                                    ^
          == 16677181699666568

其中3的模数将返回2,这将打破循环并返回false。

(我还没有检查为什么它向下舍入而不是向上,但即使它向上舍入模数仍然不为零,所以它仍然会返回false。)

答案 1 :(得分:2)

考虑一下你的算法:你想要/=做什么?

浮点除法?还是整数除法?你的问题清楚地使用前者,但是亲自尝试并看看它是否有效 - 你将继续除以base并得到非整数。

在python 3中,整数除法是//