Topics › Binary Search › BS on Answers
The N-th root of an integer
Java source ↗ Edit this article ↗
Jump to code:
Problem
You are given two positive integers n and m. Return the integer x such that x^n = m, that is, the n-th root of m. If no integer has this property, return -1.
The original SDE sheet video for this row asks a slightly different question: compute the real n-th root of m correct to 6 decimal places. The same idea solves both, so the article covers the integer version first and the real-valued version at the end.
Constraints: 1 ≤ n ≤ 30, 1 ≤ m ≤ 10^9.
Example 1
n = 3, m = 27
answer = 3 // 3 * 3 * 3 = 27
Example 2
n = 4, m = 69
answer = -1 // 2^4 = 16 < 69 < 81 = 3^4, so no integer works
Example 3
n = 1, m = 14
answer = 14 // the first root of a number is the number itself
Approach 1: Brute force, try every candidate
Intuition. For x ≥ 1 and n ≥ 1, the function x^n is strictly increasing: a bigger base always gives a bigger power. So we can walk x = 1, 2, 3, ... and stop at the first x whose power reaches or passes m. If it lands exactly on m, that x is the root. If it jumps past m, every later x overshoots even more, so no integer root exists.
Both approaches need to compare x^n with m, and that comparison hides the real trap of this problem: x^n overflows almost immediately. With x = 1000 and n = 30 the true power has 91 digits. The helper below multiplies one factor at a time in long and gives up as soon as the running product exceeds m. Before each multiplication the product is at most m ≤ 10^9 and x ≤ 10^9, so the product never exceeds 10^18, which fits in a long.
static int comparePower(long x, int n, long m) {
long result = 1;
for (int i = 0; i < n; i++) {
result *= x;
if (result > m) return 1; // stop early: one more multiplication could overflow
}
return result == m ? 0 : -1;
}
Algorithm.
- For
x = 1, 2, 3, ...: comparex^nwithm. - If they are equal, return
x. - If
x^n > m, return-1.
static int bruteForce(int n, int m) {
for (long x = 1; ; x++) {
int c = comparePower(x, n, m);
if (c == 0) return (int) x;
if (c > 0) return -1; // x^n jumped past m, so no integer root exists
}
}
Complexity. Time O(n · m^(1/n)): we stop once x reaches about m^(1/n), and each comparison costs up to n multiplications. Space O(1).
That bound looks small for large n, but the worst case is n = 1, where the loop walks all the way to m: a billion iterations for m = 10^9. For n = 2 it is about 31,623 iterations, which is fine. We want something that does not depend on m^(1/n) at all.
Approach 2: Optimal, binary search on the answer
Intuition. The monotonicity we used to stop the scan early is exactly what binary search needs. Over the candidates 1..m, the comparison x^n vs m reads “smaller, smaller, …, (maybe equal), bigger, bigger”. Look at the middle candidate: if its power is too small, the root (if any) is to the right; if too big, it is to the left. Each probe throws away half of the remaining candidates.
Why is m a safe upper bound? For x ≥ 2, x^n ≥ x, so any root of m is at most m. For m = 1 the root is 1, which is also inside [1, m].
Algorithm.
- Set
lo = 1,hi = m. - While
lo ≤ hi: letmid = lo + (hi - lo) / 2and comparemid^nwithm.- Equal: return
mid. - Smaller: the root must be bigger, so
lo = mid + 1. - Bigger: the root must be smaller, so
hi = mid - 1.
- Equal: return
- If the range empties, no integer root exists: return
-1.
static int optimal(int n, int m) {
long lo = 1, hi = m;
while (lo <= hi) {
long mid = lo + (hi - lo) / 2;
int c = comparePower(mid, n, m);
if (c == 0) return (int) mid;
if (c < 0) lo = mid + 1; else hi = mid - 1;
}
return -1;
}
Dry run on Example 2, n = 4, m = 69:
| lo | hi | mid | mid^4 vs 69 | action |
|---|---|---|---|---|
| 1 | 69 | 35 | bigger | hi = 34 |
| 1 | 34 | 17 | bigger | hi = 16 |
| 1 | 16 | 8 | bigger (4096) | hi = 7 |
| 1 | 7 | 4 | bigger (256) | hi = 3 |
| 1 | 3 | 2 | smaller (16) | lo = 3 |
| 3 | 3 | 3 | bigger (81) | hi = 2 |
| 3 | 2 | range empty | return -1 |
Notice that comparePower(35, 4, 69) stops after two multiplications (35 · 35 = 1225 > 69), so the huge candidates near the top of the range cost almost nothing.
Complexity. Time O(n log m): about log2(10^9) ≈ 30 probes, each with at most n multiplications. Space O(1).
Variant: the real-valued n-th root
When the problem asks for the real root to 6 decimal places, the candidates are no longer integers, but the shape of the argument is the same: x^n is still increasing on [0, ∞), so we can bisect the interval [0, max(1, m)]. If mid^n < m the root lies in the right half, otherwise in the left half.
Two details change with doubles. First, there is no “equal” case to stop on, and lo == hi may never happen because of rounding. A fixed number of halvings is the robust stopping rule: 100 halvings shrink an interval of length 10^9 by a factor of 2^100 ≈ 10^30, far below 10^-6 and in fact below the spacing of double values. (Looping while (hi - lo > 1e-7) also works.) Second, the product can overflow to Infinity for large mid and n. That is harmless here: Infinity < m is false, so the search correctly moves left.
static double realRoot(int n, int m) {
double lo = 0, hi = Math.max(1.0, m); // the root of m >= 0 lies in [0, max(1, m)]
for (int iter = 0; iter < 100; iter++) {
double mid = (lo + hi) / 2, p = 1;
for (int i = 0; i < n; i++) p *= mid;
if (p < m) lo = mid; else hi = mid;
}
return hi;
}
Complexity. Time O(100 · n), which is O(n) with a large constant. Space O(1).
Why not just call Math.pow(m, 1.0 / n) and round? For the real-valued version that is a fine one-liner. For the integer version it is a classic bug: in Java Math.pow(64, 1.0 / 3) evaluates to 3.9999999999999996, so casting to int gives 3 and you would wrongly report that 64 has no integer cube root. Exact integer arithmetic, as in comparePower, avoids the question entirely.
Edge cases and pitfalls
m = 1. The answer is1for everyn. Both methods find it immediately because1^n = 1.n = 1. The answer ismitself. The brute force walks allmcandidates; binary search needs only about 30 probes.- Overflow in the power. Even
mid * midoverflowsintoncemid > 46340. The tests includen = 2,m = Integer.MAX_VALUE(answer-1) andn = 2,m = 999950884(answer31622). Multiplying inlongand stopping as soon as the product exceedsmkeeps every intermediate value at mostm · m, which fits in alongfor anyintvalue ofm. - Overflow in the midpoint. Write
lo + (hi - lo) / 2. Hereloandhiarelong, but the habit matters when they areint. - Large
nwith tiny roots.n = 30,m = 10^9has no root:2^30 = 1073741824already overshoots.n = 29,m = 536870912has root2. - Floating point. Do not derive the integer answer from
Math.pow; rounding can land on either side of the true root.
Related problems
- Find Nth root of a number is the same question in the A2Z sheet.
- Find square root of a number is the
n = 2case, asking for the floor instead of an exact root. - Koko eating bananas uses the same “binary search over a monotonic yes/no question” pattern.
- Allocate Minimum Number of Pages applies binary search on the answer to a minimise-the-maximum problem.