Given two numbers N and M, find the Nth root of M. The Nth root of a number M is defined as a number X such that when X is raised to the power of N, it equals M. If the Nth root is not an integer, return -1.
Input: N = 3, M = 27
Output: 3
Explanation: The cube root of 27 is equal to 3.
Input: N = 4, M = 69
Output:-1
Explanation: The 4th root of 69 does not exist. So, the answer is -1.
Input: N = 4, M = 81
Perform a simple linear search in range [1,M]. Calculate the value of x raised to the power N for every number x in this range. If it is equal to the given number then, x is the Nth root of the number. If no such number(x) exists, return -1 as an answer.
Working of nthRoot(N, M):
#include <bits/stdc++.h>
using namespace std;
class Solution {
private:
/* Function to calculate power using
exponentiation by squaring method*/
long long Pow(int b, int exp) {
long long ans = 1;
long long base = b;
// Exponentiation by squaring method
while (exp > 0) {
if (exp % 2) {
exp--;
ans = ans * base;
} else {
exp /= 2;
base = base * base;
}
}
return ans;
}
public:
/* Function to find the nth root
of m using linear search*/
int NthRoot(int N, int M) {
// Linear search on the answer space
for (int i = 1; i <= M; i++) {
long long val = Pow(i, N);
/* Check if the computed
value is equal to M */
if (val == M * 1ll) {
// Return the root value
return i;
} else if (val > M * 1ll) {
break;
}
}
// Return -1 if no root found
return -1;
}
};
int main() {
int n = 3, m = 27;
// Create an object of the Solution class
Solution sol;
int ans = sol.NthRoot(n, m);
// Print the result
cout << "The answer is: " << ans << "\n";
return 0;
}
import java.util.*;
class Solution {
/* Function to calculate power using
exponentiation by squaring method */
private long Pow(int b, int exp) {
long ans = 1;
long base = b;
// Exponentiation by squaring method
while (exp > 0) {
if (exp % 2 == 1) {
exp--;
ans *= base;
} else {
exp /= 2;
base *= base;
}
}
return ans;
}
/* Function to find the nth
root of m using linear search */
public int NthRoot(int N, int M) {
// Linear search on the answer space
for (int i = 1; i <= M; i++) {
long val = Pow(i, N);
/* Check if the computed
value is equal to M*/
if (val == M) {
// Return the root value
return i;
} else if (val > M) {
break;
}
}
// Return -1 if no root found
return -1;
}
public static void main(String[] args) {
int n = 3, m = 27;
// Create an object of the Solution class
Solution sol = new Solution();
int ans = sol.NthRoot(n, m);
// Print the result
System.out.println("The answer is: " + ans);
}
}
class Solution:
""" Function to calculate power using
exponentiation by squaring method"""
def Pow(self, b, exp):
ans = 1
base = b
# Exponentiation by squaring method
while exp > 0:
if exp % 2 == 1:
exp -= 1
ans *= base
else:
exp //= 2
base *= base
return ans
""" Function to find the nth root
root of m using linear search"""
def NthRoot(self, N: int, M: int) -> int:
# Linear search on the answer space
for i in range(1, M + 1):
val = self.Pow(i, N)
""" Check if the computed
value is equal to M"""
if val == M:
# Return the root value
return i
elif val > M:
break
# Return -1 if no root found
return -1
# Driver code
if __name__ == "__main__":
n, m = 3, 27
# Create an object of the Solution class
sol = Solution()
ans = sol.NthRoot(n, m)
# Print the result
print(f"The answer is: {ans}")
class Solution {
/* Function to calculate power using
exponentiation by squaring method */
Pow(b, exp) {
let ans = 1;
let base = b;
// Exponentiation by squaring method
while (exp > 0) {
if (exp % 2 === 1) {
exp--;
ans *= base;
} else {
exp /= 2;
base *= base;
}
}
return ans;
}
/* Function to find the nth
root of m using linear search */
NthRoot(N, M) {
// Linear search on the answer space
for (let i = 1; i <= M; i++) {
let val = this.Pow(i, N);
/* Check if the computed
value is equal to m */
if (val === M) {
// Return the root value
return i;
} else if (val > M) {
break;
}
}
// Return -1 if no root found
return -1;
}
}
let n = 3, m = 27;
// Create an object of the Solution class
let sol = new Solution();
let ans = sol.NthRoot(n, m);
// Print the result
console.log(`The answer is: ${ans}`);
Time Complexity: O(N*logN)
The for loop runs takes O(M) time and calculating Pow(x, N) takes O(logN) time. However, since the for loop breaks as soon as the result of Pow(x, N) becomes greater than the M, thus, the for loops actually runs only for N iterations making overall complexity as O(N*logN).
Space Complexity: O(1), as there are only a couple of variables used.
The idea here is to use binary search to optimize the solution. Although the traditional application of binary search involves a sorted array, upon closer observation, one can notice that the search space for the answer here ranges from 1 to M, which inherently forms a sorted sequence. So, binary search can be applied.
Need of a helper function to find the exponent of the number:
If the given numbers M and N are big enough, the value of midN can not be stored in a variable. So to
resolve this problem, implement a helper function.
#include <bits/stdc++.h>
using namespace std;
class Solution {
private:
/* Function to calculate power using
exponentiation by squaring method*/
int helperFunc(int mid, int n, int m) {
long long ans = 1, base = mid;
while (n > 0) {
if (n % 2) {
ans = ans * base;
if (ans > m) return 2; // Early exit
n--;
}
else {
n /= 2;
base = base * base;
if(base > m) return 2;
}
}
if (ans == m) return 1;
return 0;
}
public:
// Function to find the Nth root of M
int NthRoot(int N, int M) {
// Binary search on the answer space
int low = 1, high = M;
while (low <= high) {
int mid = (low + high) / 2;
int midN = helperFunc(mid, N, M);
if (midN == 1) {
// Return mid if mid^N== M
return mid;
}
else if (midN == 0) {
// Move to the right half if mid^N < M
low = mid + 1;
}
else {
// Move to the left half if mid^N > M
high = mid - 1;
}
}
// Return -1 if no nth root found
return -1;
}
};
int main() {
int n = 3, m = 27;
// Create an object of the Solution class
Solution sol;
// Function call to find the Nth root of M
int ans = sol.NthRoot(n, m);
// Print the result
cout << "The answer is: " << ans << "\n";
return 0;
}
import java.util.*;
class Solution {
// Helper function to check mid^N compared to M
private int helperFunc(int mid, int n, int m) {
long ans = 1, base = mid;
while (n > 0) {
if (n % 2 == 1) {
ans *= base;
if (ans > m) return 2; // Early exit
n--;
} else {
n /= 2;
base *= base;
if (base > m) return 2;
}
}
if (ans == m) return 1;
return 0;
}
// Function to find the Nth root of M using Binary Search
public int NthRoot(int N, int M) {
int low = 1, high = M;
while (low <= high) {
int mid = (low + high) / 2;
int midN = helperFunc(mid, N, M);
if (midN == 1) return mid; // Found exact root
else if (midN == 0) low = mid + 1; // Move right
else high = mid - 1; // Move left
}
return -1; // No integer root found
}
}
class Main {
public static void main(String[] args) {
int n = 3, m = 27;
Solution sol = new Solution();
int ans = sol.NthRoot(n, m);
System.out.println("The answer is: " + ans);
}
}
class Solution:
# Helper function to check mid^N compared to M
def helperFunc(self, mid, n, m):
ans, base = 1, mid
while n > 0:
if n % 2 == 1:
ans *= base
if ans > m:
return 2 # Early exit
n -= 1
else:
n //= 2
base *= base
if base > m:
return 2
if ans == m:
return 1
return 0
# Function to find the Nth root of M using Binary Search
def NthRoot(self, N, M):
low, high = 1, M
while low <= high:
mid = (low + high) // 2
midN = self.helperFunc(mid, N, M)
if midN == 1:
return mid # Found exact root
elif midN == 0:
low = mid + 1 # Move right
else:
high = mid - 1 # Move left
return -1 # No integer root found
# Test case
n, m = 3, 27
sol = Solution()
ans = sol.NthRoot(n, m)
print("The answer is:", ans)
class Solution {
// Helper function to check mid^N compared to M
helperFunc(mid, n, m) {
let ans = 1, base = mid;
while (n > 0) {
if (n % 2 === 1) {
ans *= base;
if (ans > m) return 2; // Early exit
n--;
} else {
n = Math.floor(n / 2);
base *= base;
if (base > m) return 2;
}
}
if (ans === m) return 1;
return 0;
}
// Function to find the Nth root of M using Binary Search
NthRoot(N, M) {
let low = 1, high = M;
while (low <= high) {
let mid = Math.floor((low + high) / 2);
let midN = this.helperFunc(mid, N, M);
if (midN === 1) return mid; // Found exact root
else if (midN === 0) low = mid + 1; // Move right
else high = mid - 1; // Move left
}
return -1; // No integer root found
}
}
// Test case
let n = 3, m = 27;
let sol = new Solution();
let ans = sol.NthRoot(n, m);
console.log("The answer is:", ans);
Time Complexity: O(logM * logN)
The binary search on the search space (of size M) takes O(logM) and the helper function takes O(logN) taking overall O(logM * logN).
Space Complexity: O(1), as there are only a couple of variables used.
Q: What happens if M is not a perfect N-th power?
A: Binary search will terminate without finding mid^N=M. Return −1 in such cases, as the N-th root is not an integer.
Q: How do we avoid integer overflow when calculating mid^N?
A: Instead of directly calculating mid^N, compute mid^N iteratively: Multiply mid^N times while checking if the result exceeds M. Stop early if overflow is detected.
Q: What if M and N are very large?
A: For large M, calculate mid^N iteratively to prevent overflow. For large N, reduce the problem to smaller powers using modular arithmetic or logarithms (log(M)/N) to estimate the root.
Q: How would you extend this to multi-dimensional data?
A: For matrices or tensors, compute the root element-wise using the same binary search logic. If the root must satisfy additional conditions (e.g., orthogonality), integrate those into the solution.