Skip to content

69. Sqrt(X)

from typing import Callable


class Solution:
    """√ Sqrt(x).

    Problem:
    --------
    Given a non-negative integer `x`, return its square root rounded down to the nearest
    integer. Built-in exponent functions and operators (e.g. `pow(x, 0.5)`, `x ** 0.5`)
    are not allowed.

    Key Insight:
    ------------
    We want the largest `k` with `k * k <= x`. Equivalently, find the first `k` with
    `k * k > x` and step back by one. The predicate `P(k) = k * k > x` is monotone:

        k:    0  1  2  ...  x + 1
        P(k): F  F  F  T  T  T

    Approach:
    ---------
    - Run the `first_true` template over [0, x + 2). The first True is at most `x + 1`,
      since `(x + 1)^2 > x` for every `x >= 0`.
    - Subtract 1 to go from "first k with k^2 > x" to "last k with k^2 <= x".
    - `x = 0`: the first True is `k = 1`, so the answer is 0.

    Complexity:
    -----------
    - Time: O(log x)
    - Space: O(1)
    """

    def mySqrt(self, x: int) -> int:
        def first_true(low: int, high: int, predicate: Callable[[int], bool]) -> int:
            while low < high:
                mid = (low + high) // 2
                if predicate(mid):
                    high = mid
                else:
                    low = mid + 1
            return low

        return first_true(0, x + 2, lambda k: k * k > x) - 1