You’d win that bet. (guess + N // guess) // 2 is the same as (guess*guess + N) // (2 * guess), and that’s equal to guess - 1 if and only if 2 * guess * (guess - 1) <= guess*guess + N < 2 * guess * guess. That rearranges to (guess - 1)**2 - 1 <= N < guess**2, so either isqrt(N) = guess - 1, or N = (guess - 1)**2 - 1, the next guess is guess - 2, and isqrt(N) = guess - 2.
Ah, right; seems like we’ve got crossed wires here - @tim.one and I were talking about computing the floor of the square root, but you’re looking at computing the nearest perfect square to the initial value (which was indeed what @ccatcclawz originally asked for).
As it turns out, there’s a simple variant of the standard integer form of Heron’s algorithm that computes the nearest integer to the square root, and doesn’t suffer from the awkward termination condition problem - it always converges, from any positive integer starting guess, so we can just stop when the new value is equal to the old.
def sqrt_nearest(n: int, a: int = 1) -> int:
"""Find the closest integer to the square root of a positive integer n."""
while True:
a_old, a = a, a -- n // a >> 1
if a == a_old:
return a
(A form of this was used in the original Python 2.x decimal library.)
Indeed it does! I confess to being surprised, and a little horrified. There’s no obvious reason that we couldn’t end up oscillating between two different (but very close) values for E, so that E2 == E is never true. However, it turns out that there are nonobvious reasons that that can’t happen. Essentially, we always converge to a situation where N/E and E are either equal, or exactly one unit in the last place apart. And once we’ve hit that point, the averaging operation (E + N / E) / 2 will always prefer whichever of E and N/E has even last bit, thanks to the default round-ties-to-even rounding mode for IEEE 754 floating-point arithmetic.
By the way, your loopRepeating construct is a perfect use-case for Python’s break statement: you can rewrite your while loop like this:
while True:
E = (E + N / E) / 2
if E2 == E:
break
E2 = E