检查完美正方形的功能不适用于大量
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;
}
这适用于 256
、808201
等数字
但对于 999966000289
.
这样的数字失败
我不明白为什么?
如评论中所述,问题是中间mid*mid
可能会溢出。这将有助于使用无符号类型和“long”或“long long”变体。
然而,low
和high
的初始值,mid
的第一个值接近x/4
。如果 x
很大,这是对平方根的很大超调。
因此,我们可以通过改进初始 low
和 high
限制估计来扩大可管理数字的范围。
免责声明: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<2
,x=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
循环后平方根的上限需要更长的分析。
我遇到了一种算法,可以在 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;
}
这适用于 256
、808201
等数字
但对于 999966000289
.
我不明白为什么?
如评论中所述,问题是中间mid*mid
可能会溢出。这将有助于使用无符号类型和“long”或“long long”变体。
然而,low
和high
的初始值,mid
的第一个值接近x/4
。如果 x
很大,这是对平方根的很大超调。
因此,我们可以通过改进初始 low
和 high
限制估计来扩大可管理数字的范围。
免责声明: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<2
,x=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
循环后平方根的上限需要更长的分析。