BS-11. Find the Nth Root of an Integer
1. Problem Statement & Overview
Given two integers and , our objective is to find the -th root of . The -th root of an integer is a number such that multiplying by itself times yields ().
- Example 1: If and , the 3rd root of 27 is because .
- Example 2: If and , there is no integer whose 4th power equals 69 (, , ). In such cases where an exact integer root does not exist, the program must return .
2. Naive Approach: Linear Search
The most intuitive way to solve this is to test integers sequentially starting from up to .
How It Works
- Run a loop variable from to .
- Compute (using either a simple multiplication loop or binary exponentiation).
- If , return .
- If , it means we have crossed the target value. Since further increments will only make the product larger, we can immediately break out of the loop and return .
Naive Code Structure
int linearSearchNthRoot(int n, int m) {
for (int i = 1; i <= m; i++) {
long long val = computePower(i, n);
if (val == m) return i;
if (val > m) break; // Exceeded target, no exact root exists
}
return -1;
}
Complexity Analysis
- Time Complexity: if using binary exponentiation to calculate , or if using a simple loop for exponentiation. In the worst case, the algorithm iterates up to .
- Space Complexity: auxiliary space.
3. Core Mental Model: Why Binary Search on Answers?
The linear search approach is too slow when is large (e.g., ). We need a faster way to eliminate large blocks of candidate integers.
Real-Life Analogy: The Price Guessing Game
Imagine playing a game where you must guess a hidden target number between and . If you guess and the host says "Too high," you instantly discard and everything above it, cutting your search space in half.
This exact monotonic property applies to finding the -th root:
- If our current candidate mid raised to the power () is less than , our answer must lie to the right (we need a larger number).
- If is greater than , our answer and anything larger can never be valid. We must discard the right half and move to the left.
4. Step-by-Step Evolution: Binary Search on Answers
By recognizing that the search space is strictly sorted and monotonic, we can apply Binary Search.
Algorithmic Steps
- Define the Search Space: Set and .
- Find the Midpoint: .
- Evaluate : Compare the computed power against .
- If , we found our exact root; return .
- If , the root must be larger; shift .
- If , the root must be smaller; shift .
- Termination: If crosses without finding an exact match, return .
5. Walkthrough 1: Successful Search ()
Let's trace the algorithm step-by-step for finding the 3rd root of 27:
- Initial Range: ,
Iteration 1:
- mid = (1 + 27) / 2 = 14
- 14^3 = 2744 (Greater than 27)
- Action: Eliminate everything >= 14. High moves to 13.
Iteration 2:
- low = 1, high = 13
- mid = (1 + 13) / 2 = 7
- 7^3 = 343 (Greater than 27)
- Action: Eliminate everything >= 7. High moves to 6.
Iteration 3:
- low = 1, high = 6
- mid = (1 + 6) / 2 = 3 (integer division)
- 3^3 = 27 (Equal to 27!)
- Action: Return 3 as the exact Nth root.
6. Walkthrough 2: Unsuccessful Search ()
Let's trace an example where no integer root exists (4th root of 69):
- Initial Range: ,
7. Real-World Practical Pitfalls: Integer Overflow
When computing for large constraints (e.g., finding the 10th root of ), standard integer types will overflow.
The Overflow Problem
If and , can be around . Raising to the 10th power produces a number near , which far exceeds 64-bit integer limits (long long in C++).
The Solution: Controlled Multiplication with Early Exit
Instead of computing fully in one go and risking an overflow, we compute it iteratively while multiplying. The moment our accumulated product exceeds , we abort early and return a flag indicating that the value is too large.
// Returns: 2 if (mid^n > m), 1 if (mid^n == m), 0 if (mid^n < m)
int func(long long mid, int n, int m) {
long long ans = 1;
for (int i = 1; i <= n; i++) {
ans = ans * mid;
if (ans > m) return 2; // Early exit to prevent overflow
}
if (ans == m) return 1;
return 0;
}
8. Complete Optimized Implementation
Below is the robust, production-ready solution implementing Binary Search on Answers with overflow prevention:
class Solution {
private:
// Helper function to calculate mid^n with overflow protection
int powerCheck(long long mid, int n, int m) {
long long ans = 1;
for (int i = 1; i <= n; i++) {
ans = ans * mid;
if (ans > m) return 2; // Exceeds m
}
if (ans == m) return 1; // Exactly equals m
return 0; // Less than m
}
public:
int NthRoot(int n, int m) {
long long low = 1, high = m;
while (low <= high) {
long long mid = low + (high - low) / 2;
int res = powerCheck(mid, n, m);
if (res == 1) {
return mid; // Exact root found
} else if (res == 2) {
high = mid - 1; // Mid^n is too large, move left
} else {
low = mid + 1; // Mid^n is too small, move right
}
}
return -1; // No integer nth root exists
}
};
9. Summary and Key Takeaways
- Problem Recognition: When asked to find a specific boundary value or root within a known range , Binary Search on Answers is the optimal strategy.
- Search Space Definition: Always initialize and to cover all potential integer solutions.
- Monotonic Elimination: If , discard the right half (); if , discard the left half ().
- Efficiency: Reduces time complexity from linear to logarithmic , making it capable of handling extremely large inputs efficiently.
- Production Safety: Always watch out for integer overflow when computing powers; use iterative multiplication with an early exit threshold (
> m) rather than native exponentiation functions.