I came across an algorithm to tell if a given number is perfect square or not in O(logN) time.
Here is the implementation(JAVA) of the idea.
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;
}
This works fine for numbers like 256, 808201 , etc
But fails for numbers like 999966000289.
I cannot figure out why?
As mentioned in the comments, the problem is that the intermediate
mid*midmay overflow. It will help to use an unsigned type and a "long" or "long long" variant.However, with the initial values of
lowandhigh, the first value ofmidis close tox/4. Ifxis large, this is a great overshoot of the square root.Hence, we can improve the range of manageable numbers by improving the initial
lowandhighlimit estimates.Disclaimer: The Stack Overflow format is not suitable for long analysis. I have a good argument that the following works, part of which I have included below, but the full analysis is too lengthy to include here.
With this modification, the initial value of
midis much smaller for large values ofxand thus larger values ofxcan be handled without overflow.It is not so difficult to show that the lower limit will not exceed the square root and doing that illustrates the intuition behind this method:
For some
t, where1<=t<2,x=t*2^rfor some integer,r. Thus:which implies that
Thus a lower limit is a binary
1shifted until it gets halfway (whenris even) or as close as possible (whenris odd) to the leftmost 1-bit in the binary representation ofx. This is exactly what happens in thewhile-loop.Showing that
highis indeed an upper bound of the square root after thewhile-loop requires a longer analysis.