483. Smallest Good Base
Problem Description
Given an integer n as a string, find the smallest base k (where k ≥ 2) such that when n is represented in base k, all its digits are 1's.
In other words, we need to find the smallest integer k where:
n = 1 + k + k² + k³ + ... + k^mfor some integerm ≥ 0- This means
nin basekwould be written as "111...1" (all 1's)
For example:
- If
n = 13, the smallest good base is3because13in base3equals111(since1×3² + 1×3¹ + 1×3⁰ = 9 + 3 + 1 = 13) - If
n = 4681, the smallest good base is8because4681 = 1 + 8 + 8² + 8³ + 8⁴ = 1 + 8 + 64 + 512 + 4096 = 4681
The solution uses binary search to find the appropriate base. For each possible number of digits m (from largest to smallest), it searches for a base k such that the sum 1 + k + k² + ... + k^m equals n. The function cal(k, m) computes this geometric series sum. By iterating through different values of m and using binary search to find the corresponding k, the algorithm finds the smallest valid base.
Intuition
The key insight is recognizing that we're looking for a base k where n = 1 + k + k² + ... + k^m. This is a geometric series with sum formula: n = (k^(m+1) - 1) / (k - 1).
Since we want the smallest base, we should start by checking if n can be represented with more digits (larger m), as having more 1's typically requires a smaller base. For instance, 111 in base 2 is smaller than 11 in base 6, even though both equal 7.
The maximum number of digits possible is around 63 because even in base 2 (the smallest valid base), we can't exceed the range of a 64-bit integer. So we iterate m from 63 down to 2.
For each fixed number of digits m, we need to find if there exists a base k such that 1 + k + k² + ... + k^m = n. As k increases, this sum grows monotonically, making binary search perfect for finding the exact k value.
The search range for k is from 2 (minimum valid base) to n-1 (maximum meaningful base, since in base n or higher, n would be represented as a single digit).
Why return str(num - 1) at the end? If no valid base is found for m ≥ 2, then we need m = 1, meaning n = 1 + k. This gives us k = n - 1, which always works as n in base n-1 equals 11 (one (n-1) plus one unit).
This approach guarantees finding the smallest good base by systematically checking from the maximum possible number of digits down to the minimum.
Solution Implementation
1class Solution:
2 def smallestGoodBase(self, n: str) -> str:
3 def calculate_sum(base: int, num_terms: int) -> int:
4 """
5 Calculate the sum: 1 + base + base^2 + ... + base^num_terms
6 """
7 power = 1
8 total = 1
9 for _ in range(num_terms):
10 power *= base
11 total += power
12 return total
13
14 def feasible(base: int, num_terms: int, target: int) -> bool:
15 """Check if sum of powers >= target (feasible condition)"""
16 return calculate_sum(base, num_terms) >= target
17
18 target = int(n)
19
20 # Try different digit counts from largest to smallest
21 for num_terms in range(63, 1, -1):
22 # Binary search template: find first base where sum >= target
23 left, right = 2, target - 1
24 first_true_index = -1
25
26 while left <= right:
27 mid = (left + right) // 2
28 if feasible(mid, num_terms, target):
29 first_true_index = mid
30 right = mid - 1 # Search for smaller base
31 else:
32 left = mid + 1
33
34 # Check if we found an exact match
35 if first_true_index != -1 and calculate_sum(first_true_index, num_terms) == target:
36 return str(first_true_index)
37
38 # Fallback: m = 1 case, n = 1 + (n-1)
39 return str(target - 1)
401class Solution {
2 public String smallestGoodBase(String n) {
3 long target = Long.parseLong(n);
4
5 // Try different digit counts from largest to smallest
6 for (int numTerms = 63; numTerms >= 2; numTerms--) {
7 long base = findBase(numTerms, target);
8 if (base != -1) {
9 return String.valueOf(base);
10 }
11 }
12
13 // Fallback: m = 1 case, n = 1 + (n-1)
14 return String.valueOf(target - 1);
15 }
16
17 /**
18 * Binary search template: find the smallest base where sum >= target,
19 * then check if it's an exact match.
20 */
21 private long findBase(int numTerms, long target) {
22 long left = 2;
23 long right = target - 1;
24 long firstTrueIndex = -1;
25
26 while (left <= right) {
27 long mid = left + (right - left) / 2;
28 if (feasible(mid, numTerms, target)) {
29 firstTrueIndex = mid;
30 right = mid - 1; // Search for smaller base
31 } else {
32 left = mid + 1;
33 }
34 }
35
36 // Check if we found an exact match
37 if (firstTrueIndex != -1 && calculateSum(firstTrueIndex, numTerms) == target) {
38 return firstTrueIndex;
39 }
40 return -1;
41 }
42
43 private boolean feasible(long base, int numTerms, long target) {
44 return calculateSum(base, numTerms) >= target;
45 }
46
47 /**
48 * Calculate: 1 + base + base^2 + ... + base^numTerms
49 * Returns Long.MAX_VALUE on overflow.
50 */
51 private long calculateSum(long base, int numTerms) {
52 long power = 1;
53 long sum = 1;
54
55 for (int i = 0; i < numTerms; i++) {
56 // Check for overflow
57 if (power > Long.MAX_VALUE / base) {
58 return Long.MAX_VALUE;
59 }
60 power *= base;
61 if (sum > Long.MAX_VALUE - power) {
62 return Long.MAX_VALUE;
63 }
64 sum += power;
65 }
66
67 return sum;
68 }
69}
701class Solution {
2public:
3 string smallestGoodBase(string n) {
4 long target = stol(n);
5
6 // Try different digit counts from largest to smallest
7 for (int numTerms = 63; numTerms >= 2; --numTerms) {
8 long base = findBase(numTerms, target);
9 if (base != -1) {
10 return to_string(base);
11 }
12 }
13
14 // Fallback: m = 1 case, n = 1 + (n-1)
15 return to_string(target - 1);
16 }
17
18private:
19 // Binary search template: find smallest base where sum >= target
20 long findBase(int numTerms, long target) {
21 long left = 2;
22 long right = target - 1;
23 long firstTrueIndex = -1;
24
25 while (left <= right) {
26 long mid = left + (right - left) / 2;
27 if (feasible(mid, numTerms, target)) {
28 firstTrueIndex = mid;
29 right = mid - 1; // Search for smaller base
30 } else {
31 left = mid + 1;
32 }
33 }
34
35 // Check if we found an exact match
36 if (firstTrueIndex != -1 && calculateSum(firstTrueIndex, numTerms) == target) {
37 return firstTrueIndex;
38 }
39 return -1;
40 }
41
42 bool feasible(long base, int numTerms, long target) {
43 return calculateSum(base, numTerms) >= target;
44 }
45
46 // Calculate: 1 + base + base^2 + ... + base^numTerms
47 long calculateSum(long base, int numTerms) {
48 long power = 1;
49 long sum = 1;
50
51 for (int i = 0; i < numTerms; ++i) {
52 // Check for overflow
53 if (power > LONG_MAX / base) {
54 return LONG_MAX;
55 }
56 power *= base;
57 if (sum > LONG_MAX - power) {
58 return LONG_MAX;
59 }
60 sum += power;
61 }
62
63 return sum;
64 }
65};
661function smallestGoodBase(n: string): string {
2 const target = parseInt(n);
3
4 // Helper: calculate 1 + base + base^2 + ... + base^numTerms
5 function calculateSum(base: number, numTerms: number): number {
6 let power = 1;
7 let sum = 1;
8 for (let i = 0; i < numTerms; i++) {
9 power *= base;
10 sum += power;
11 }
12 return sum;
13 }
14
15 // Feasible condition: sum >= target
16 function feasible(base: number, numTerms: number): boolean {
17 return calculateSum(base, numTerms) >= target;
18 }
19
20 // Binary search template: find smallest base where sum >= target
21 function findBase(numTerms: number): number {
22 let left = 2;
23 let right = target - 1;
24 let firstTrueIndex = -1;
25
26 while (left <= right) {
27 const mid = Math.floor((left + right) / 2);
28 if (feasible(mid, numTerms)) {
29 firstTrueIndex = mid;
30 right = mid - 1; // Search for smaller base
31 } else {
32 left = mid + 1;
33 }
34 }
35
36 // Check if we found an exact match
37 if (firstTrueIndex !== -1 && calculateSum(firstTrueIndex, numTerms) === target) {
38 return firstTrueIndex;
39 }
40 return -1;
41 }
42
43 // Try different digit counts from largest to smallest
44 for (let numTerms = 63; numTerms >= 2; numTerms--) {
45 const base = findBase(numTerms);
46 if (base !== -1) {
47 return base.toString();
48 }
49 }
50
51 // Fallback: m = 1 case, n = 1 + (n-1)
52 return (target - 1).toString();
53}
54Solution Approach
The implementation uses binary search within a loop to systematically find the smallest good base, following the standard binary search template.
Helper Function calculate_sum(base, num_terms):
This function calculates the geometric series sum 1 + base + base² + ... + base^num_terms. It initializes power = 1 (current power of base) and total = 1 (sum starting with the first term). Then it iterates num_terms times, multiplying power by base to get the next power and adding it to the sum.
Main Algorithm:
-
Convert the string
nto an integertargetfor numerical operations. -
Iterate through possible digit counts
mfrom 63 down to 2:- We start from 63 because that's approximately the maximum number of bits needed to represent large integers
- We check larger
mvalues first because more digits generally means a smaller base
-
For each
m, use binary search with the standard template to find the basek:- Initialize:
left = 2,right = target - 1,first_true_index = -1 - The feasible condition is
calculate_sum(mid, m) >= target - Binary search loop:
while left <= right- Calculate midpoint:
mid = (left + right) // 2 - If
feasible(mid): storefirst_true_index = mid, then search for smaller bases (right = mid - 1) - Otherwise: search larger bases (
left = mid + 1)
- Calculate midpoint:
- Initialize:
-
After binary search, check if
first_true_index != -1andcalculate_sum(first_true_index, m) == target:- If yes, we found an exact match! Return
str(first_true_index)as the smallest good base for thism - If no exact match, continue to the next smaller
m
- If yes, we found an exact match! Return
-
If no base is found for any
m >= 2, returnstr(target - 1):- This handles the case where
m = 1, meaningn = 1 + k - Therefore
k = n - 1, andnin base(n-1)equals11
- This handles the case where
The algorithm efficiently finds the smallest good base by leveraging the monotonic property of the geometric series and using binary search to quickly locate valid bases for each possible digit count.
Ready to land your dream job?
Unlock your dream job with a 5-minute evaluator for a personalized learning plan!
Start EvaluatorExample Walkthrough
Let's trace through finding the smallest good base for n = "13" using the standard binary search template.
Step 1: Convert string to integer: target = 13
Step 2: Try different digit counts m from 63 down to 2.
For m = 63, 62, ..., 4:
- When we calculate
calculate_sum(2, m)for these values, we get sums much larger than 13 - Binary search finds no exact match for any of these
mvalues - So we continue to smaller
mvalues
Step 3: When m = 3 (looking for 4 digits):
-
Initialize:
left = 2,right = 12,first_true_index = -1 -
Feasible condition:
calculate_sum(mid, 3) >= 13 -
First iteration:
mid = 7calculate_sum(7, 3) = 1 + 7 + 49 + 343 = 400 >= 13✓ (feasible)- Update:
first_true_index = 7,right = 6
-
Second iteration:
mid = 4calculate_sum(4, 3) = 1 + 4 + 16 + 64 = 85 >= 13✓ (feasible)- Update:
first_true_index = 4,right = 3
-
Third iteration:
mid = 2calculate_sum(2, 3) = 1 + 2 + 4 + 8 = 15 >= 13✓ (feasible)- Update:
first_true_index = 2,right = 1
-
Loop ends:
left > right -
Check:
calculate_sum(2, 3) = 15 ≠ 13, so no exact match form = 3
Step 4: When m = 2 (looking for 3 digits):
-
Initialize:
left = 2,right = 12,first_true_index = -1 -
Feasible condition:
calculate_sum(mid, 2) >= 13 -
First iteration:
mid = 7calculate_sum(7, 2) = 1 + 7 + 49 = 57 >= 13✓ (feasible)- Update:
first_true_index = 7,right = 6
-
Second iteration:
mid = 4calculate_sum(4, 2) = 1 + 4 + 16 = 21 >= 13✓ (feasible)- Update:
first_true_index = 4,right = 3
-
Third iteration:
mid = 2calculate_sum(2, 2) = 1 + 2 + 4 = 7 < 13✗ (not feasible)- Update:
left = 3
-
Fourth iteration:
mid = 3calculate_sum(3, 2) = 1 + 3 + 9 = 13 >= 13✓ (feasible)- Update:
first_true_index = 3,right = 2
-
Loop ends:
left > right -
Check:
calculate_sum(3, 2) = 13 == 13✓ Exact match!
Step 5: Return "3"
Indeed, 13 in base 3 equals 111 because 1×3² + 1×3¹ + 1×3⁰ = 9 + 3 + 1 = 13.
Time and Space Complexity
Time Complexity: O(log²(n))
The algorithm iterates through possible lengths m from 63 down to 2, which takes O(log(n)) iterations since the maximum useful value of m is approximately log₂(n). For each value of m, a binary search is performed on the range [2, n-1], which takes O(log(n)) iterations. Within each binary search iteration, the cal function is called, which performs m multiplications and additions, taking O(m) = O(log(n)) time. Therefore, the total time complexity is O(log(n) × log(n) × log(n)) = O(log³(n)).
However, upon closer analysis, the outer loop runs for at most 63 iterations (a constant), so we can consider it as O(1) in terms of n. The binary search takes O(log(n)) iterations, and each call to cal takes O(log(n)) time. This gives us a more precise complexity of O(log²(n)).
Space Complexity: O(1)
The algorithm uses only a constant amount of extra space for variables num, m, l, r, mid, and the variables within the cal function (p, s, i). No additional data structures that scale with the input size are used. The input is a string representation of a number, but we only store its integer conversion and a few working variables.
Common Pitfalls
1. Using Wrong Binary Search Template Variant
A common mistake is using while left < right with right = mid instead of the standard template with while left <= right and right = mid - 1.
Incorrect (non-standard template):
while left < right: mid = (left + right) // 2 if calculate_sum(mid, num_terms) >= target: right = mid else: left = mid + 1 return left
Correct (standard template):
first_true_index = -1 while left <= right: mid = (left + right) // 2 if feasible(mid, num_terms, target): first_true_index = mid right = mid - 1 else: left = mid + 1
The standard template with first_true_index clearly tracks whether a valid answer was found and handles edge cases consistently.
2. Integer Overflow in Power Calculation
When base and num_terms are large, computing base^num_terms can exceed the maximum integer value, causing incorrect results or runtime errors.
Solution: Add overflow checking to prevent invalid calculations:
def calculate_sum(base: int, num_terms: int) -> int:
power = 1
total = 1
for _ in range(num_terms):
if power > (10**18) // base:
return float('inf')
power *= base
total += power
return total
3. Not Checking for Exact Match
The binary search finds the first base where sum >= target, but we need an exact match where sum == target. Forgetting this check leads to incorrect results.
Solution: After binary search completes, always verify:
if first_true_index != -1 and calculate_sum(first_true_index, num_terms) == target:
return str(first_true_index)
4. Edge Case: n = "3"
The code returns str(target - 1) when no base is found for m >= 2. This handles m = 1 case where n = 1 + k, giving k = n - 1. For n = "3", this correctly returns "2" since 3 in base 2 is "11".
What is the best way of checking if an element exists in an unsorted array once in terms of time complexity? Select the best that applies.
Recommended Readings
Math for Technical Interviews How much math do I need to know for technical interviews The short answer is about high school level math Computer science is often associated with math and some universities even place their computer science department under the math faculty However the reality is that you
https assets algo monster cover_photos Binary_Search svg Binary Search Intuition Binary search is an efficient array search algorithm It works by narrowing down the search range by half each time If you have looked up a word in a physical dictionary you've already used binary search in real life Let's
Coding Interview Patterns Your Personal Dijkstra's Algorithm to Landing Your Dream Job The goal of AlgoMonster is to help you get a job in the shortest amount of time possible in a data driven way We compiled datasets of tech interview problems and broke them down by patterns This way
Want a Structured Path to Master System Design Too? Don’t Miss This!