检查完美正方形的功能不适用于大量

Function to check for perfect square not working for large number

我遇到了一种算法,可以在 O(logN) 时间内判断给定数字是否为完全平方数。

这是想法的实现(JAVA)。

public boolean isPerfectSquare(long x) {
        if (x <= 1)
            return true;
        
        long low = 1;
        long high = x;
        long mid = 0;
        while (low <= high) {
            mid = low + (high - low) / 2l;
            if (mid * mid == x)
                return true;
            else if (mid * mid < x)
                low = mid + 1;
            else
                high = mid - 1;
        }
        
        return false;
    }

这适用于 256808201 等数字 但对于 999966000289.

这样的数字失败

我不明白为什么?

如评论中所述,问题是中间mid*mid可能会溢出。这将有助于使用无符号类型和“long”或“long long”变体。

然而,lowhigh的初始值,mid的第一个值接近x/4。如果 x 很大,这是对平方根的很大超调。

因此,我们可以通过改进初始 lowhigh 限制估计来扩大可管理数字的范围。

免责声明:Stack Overflow 格式不适合长时间分析。我有一个很好的论据,以下是有效的,我在下面包含了其中的一部分,但完整的分析太长了,无法包含在这里。

bool isPerfectSquare(unsigned long x) {
    if (x <= 1)
        return true;
        
    unsigned long low = 1;
    unsigned long high = x;

    // Improve the low/high limits
    while((low<<1) < (high>>1))
    {
        low <<= 1;
        high >>= 1;
    }

    unsigned long mid = 0;
    while (low <= high) {
        mid = low + (high - low) / 2l;
        if (mid * mid == x)
            return true;
        else if (mid * mid < x)
            low = mid + 1;
        else
            high = mid - 1;
    }
    return false;
}

通过此修改,mid 的初始值对于较大的 x 值要小得多,因此可以在不溢出的情况下处理较大的 x 值。

证明下限不会超过平方根并不难,这样做说明了这种方法背后的直觉:

对于某些 t,其中 1<=t<2x=t*2^r 对于某些整数,r。因此:

    sqrt(x) = sqrt(t) * 2^(r/2)

这意味着

    2^(r/2) <= sqrt(x) < 2^(r/2+1)

因此,下限是二进制 1 移动到一半(当 r 为偶数时)或尽可能接近(当 r 为奇数时)到最左边x 的二进制表示中的 1 位。这正是 while 循环中发生的事情。

显示 high 确实是 while 循环后平方根的上限需要更长的分析。