The Smallest Good Base problem asks: Given an integer n
represented as a string, find the smallest good base of n
. A good base k
(where k >= 2
) is such that n
can be represented as a sum of the form:
n = 1 + k + k^2 + ... + k^m
for some integer m >= 1
(i.e., all digits in base k
are 1). The problem guarantees that there is always exactly one valid solution for each input. The goal is to return the smallest possible k
as a string.
n
(string representing a positive integer, up to 1018)k
(string)n
is always valid, and there is always one unique answer.
At first glance, one might consider brute-forcing through all possible bases from 2 up to n-1
, checking for each if n
can be written as a sum of powers of that base. However, this is highly inefficient, especially since n
can be as large as 1018.
The key insight is to recognize that the sum 1 + k + k^2 + ... + k^m
is a geometric series. This allows us to relate n
, k
, and m
mathematically, and search for possible values of k
much more efficiently.
Instead of checking every possible base, we consider the length of the representation (i.e., how many 1's there are), and for each possible length, we try to find a suitable base using mathematical formulas and binary search.
n = 1 + k + k^2 + ... + k^m = (k^{m+1} - 1) / (k - 1)
(geometric sum formula).
m
(number of digits minus one), try to find an integer base k
such that the above equation holds.
m
is floor(log_2 n)
, since the smallest base is 2 (which gives the longest possible representation).m
from largest to smallest, use binary search to find k
in the range [2, n1/m + 1].
k
, compute (k^{m+1} - 1) / (k - 1)
and compare to n
.k
is a good base.n-1
(since n = 1 + (n-1)
in base n-1
).This approach is efficient because it reduces the number of candidates to check by orders of magnitude and leverages the properties of geometric series and binary search.
Let's take n = "13"
as an example.
n
to integer: n = 13
.
max_m = floor(log_2 13) = 3
(since 2^3 = 8, 2^4 = 16 > 13).
m = 3
(4 digits):
k
in [2, n1/3 + 1] = [2, 3]k = 2
: sum = (2^4 - 1) / (2 - 1) = (16 - 1) / 1 = 15k = 3
: sum = (3^4 - 1) / (3 - 1) = (81 - 1) / 2 = 40m = 2
(3 digits):
k
in [2, n1/2 + 1] = [2, 4]k = 2
: (2^3 - 1) / (2 - 1) = (8 - 1) / 1 = 7k = 3
: (3^3 - 1) / (3 - 1) = (27 - 1) / 2 = 13k = 3
is a good base.m
down, the first base found is the smallest good base. Return "3".
m
up to log2 n (about 60 for n up to 1018), do a binary search over a small range.m
values.import math
class Solution:
def smallestGoodBase(self, n: str) -> str:
n = int(n)
max_m = n.bit_length() # log2(n), maximum possible m+1
for m in range(max_m, 1, -1):
k = int(n ** (1 / (m - 1)))
if k < 2:
continue
# Compute sum = (k^m - 1) // (k - 1)
s = 1
curr = 1
for _ in range(m - 1):
curr *= k
s += curr
if s == n:
return str(k)
return str(n - 1)
#include <cmath>
#include <string>
using namespace std;
class Solution {
public:
string smallestGoodBase(string n) {
unsigned long long num = stoull(n);
int max_m = log2(num) + 1;
for (int m = max_m; m >= 2; --m) {
unsigned long long k = pow(num, 1.0 / (m - 1));
if (k < 2) continue;
unsigned long long s = 1, curr = 1;
for (int i = 1; i < m; ++i) {
curr *= k;
s += curr;
}
if (s == num) return to_string(k);
}
return to_string(num - 1);
}
};
class Solution {
public String smallestGoodBase(String n) {
long num = Long.parseLong(n);
int max_m = (int)(Math.log(num) / Math.log(2)) + 1;
for (int m = max_m; m >= 2; --m) {
long k = (long)Math.pow(num, 1.0 / (m - 1));
if (k < 2) continue;
long s = 1, curr = 1;
for (int i = 1; i < m; ++i) {
curr *= k;
s += curr;
}
if (s == num) return String.valueOf(k);
}
return String.valueOf(num - 1);
}
}
var smallestGoodBase = function(n) {
const num = BigInt(n);
const max_m = Math.floor(Math.log2(Number(num))) + 1;
for (let m = max_m; m >= 2; --m) {
let k = BigInt(Math.floor(Number(num) ** (1 / (m - 1))));
if (k < 2n) continue;
let s = 1n, curr = 1n;
for (let i = 1; i < m; ++i) {
curr *= k;
s += curr;
}
if (s === num) return k.toString();
}
return (num - 1n).toString();
};
The Smallest Good Base problem leverages the geometric series formula to efficiently search for the minimal base in which a number can be represented as a string of all 1's. By iterating over possible lengths of the representation and using binary search (or direct computation) for the base, we avoid brute-force checks and achieve excellent performance. The solution is both mathematically elegant and computationally efficient, making it a great example of combining mathematical insight with programming techniques.