Algorithm tutorials
Number Bases, Base Conversion and Gray Code
Convert between number systems: repeated divmod and Horner's rule for any base up to 36, negative bases such as base -2, the bijective base 26 behind spreadsheet column titles, mixed radix, and Gray code with its inverse.
A number written in base b is a list of digits d_k ... d_1 d_0 with value d_k * b^k + ... + d_1 * b + d_0. Interview questions test whether you can move between representations: decimal to base 7 or base 16, spreadsheet column titles (a base-26 system with no zero digit), base -2, and Gray code, where consecutive numbers differ in exactly one bit.
Intuition
- Number to digits: the last digit is
n mod b, andn // bholds the remaining digits. Repeating divmod produces the digits from least to most significant, so reverse at the end. - Digits to number (Horner's rule): read from the most significant digit,
value = value * b + digit. Each step shifts what you have one position left. - Powers-of-two bases: one hex digit is exactly 4 bits and one octal digit 3 bits, so conversion between bases 2, 8 and 16 is regrouping bits.
- Bijective base 26 (spreadsheet columns A..Z, AA, AB, ...): digits run from 1 to 26 and there is no zero. Subtracting 1 before each divmod maps the digits onto 0..25 and makes the usual algorithm work.
- Negative base: with base -2, every integer (positive or negative) has a unique representation using digits 0 and 1. Division still works, but a negative remainder must be fixed by adding
|b|to it and 1 to the quotient. - Mixed radix: each position may have its own base, e.g. seconds/minutes/hours (60, 60, 24) or the factorial number system that ranks permutations.
- Gray code:
g = n ^ (n >> 1)lists all k-bit numbers so that neighbors (including the last and the first) differ in one bit. It is the "reflected" sequence: the code for k bits is the code for k - 1 bits followed by its mirror image with the top bit set.
When to use it in interviews / OAs
- "Convert an integer to base 7 / hexadecimal / binary string", "convert to base -2".
- "Column number to spreadsheet title and back".
- "Generate an n-bit sequence where adjacent values differ by one bit".
- Parsing numbers from strings without built-ins; checking whether a number is a sum of distinct powers of 3 (its base-3 digits must all be 0 or 1).
Template
DIGITS = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ"
def to_base(n: int, b: int) -> str:
# 2 <= b <= 36; digits come out least significant first
if n == 0:
return "0"
sign, n = ("-", -n) if n < 0 else ("", n)
out = []
while n:
n, r = divmod(n, b)
out.append(DIGITS[r])
return sign + "".join(reversed(out))
def from_base(s: str, b: int) -> int:
# Horner's rule: shift everything one place left, then add the next digit
sign, s = (-1, s[1:]) if s.startswith("-") else (1, s)
value = 0
for ch in s:
value = value * b + DIGITS.index(ch.upper())
return sign * value
def to_negabase(n: int, b: int = -2) -> str:
# negative base: every digit must stay in 0..|b|-1
if n == 0:
return "0"
out = []
while n:
n, r = divmod(n, b) # in Python the remainder takes the sign of b, so r <= 0
if r < 0:
r -= b # move the digit into 0..|b|-1 ...
n += 1 # ... and compensate in the quotient
out.append(DIGITS[r])
return "".join(reversed(out))
In Python, int(s, b), bin, oct, hex and format(n, "x") cover the standard bases; interviews usually ask you to write the loop anyway.
Spreadsheet columns: base 26 without a zero
def column_title(n: int) -> str:
# bijective base 26: digits are 1..26 (A..Z), there is no zero digit
out = []
while n:
n, r = divmod(n - 1, 26) # shift the digit range to 0..25 before dividing
out.append(chr(ord("A") + r))
return "".join(reversed(out))
def column_number(title: str) -> int:
value = 0
for ch in title:
value = value * 26 + (ord(ch) - ord("A") + 1)
return value
Without the - 1, 26 would become "A@" or "BA" instead of "Z": the classic off-by-one of this problem.
Gray code
def gray_code(k: int) -> list[int]:
# all k-bit numbers; consecutive entries (and the last and first) differ in one bit
return [i ^ (i >> 1) for i in range(1 << k)]
def gray_to_binary(g: int) -> int:
# inverse transform: binary bit i is the XOR of Gray bits i and above
n = 0
while g:
n ^= g
g >>= 1
return n
Why does it work? Going from i to i + 1 flips a block of trailing ones to zeros plus one zero to one. In i ^ (i >> 1) every flipped bit except the highest one is cancelled by the flip of its neighbor, so exactly one bit of the Gray code changes. To start the cycle at a given value s, XOR every entry with s; the one-bit-difference property is preserved.
Complexity
Conversions take O(number of digits) = O(log_b n) operations; Gray code generation is O(2^k). Parsing a string of length L costs O(L) (with big integers, each step is proportional to the current size).
Common pitfalls
- Zero: the divmod loop produces an empty string for 0; return "0" explicitly.
- Negative numbers: convert the absolute value and prepend the sign, unless the problem wants a two's complement representation (for example a 32-bit hex string: mask with
0xFFFFFFFFfirst). - Digit order: divmod yields the least significant digit first.
- No-zero systems: spreadsheet titles need the
n - 1shift; Z is 26, AA is 27. - Negative bases: Python and C++ disagree on the sign of the remainder, but the fix (adjust when
r < 0) handles both. - Digits above 9: use letters, and accept both cases when parsing.
Interview variations
- Base 7, convert a number to hexadecimal (two's complement for negatives), convert to base -2.
- Spreadsheet column title and column number.
- Gray code and circular permutation in binary representation (Gray code XOR the start value).
- Check if a number is a sum of powers of three: all base-3 digits are 0 or 1.
- Add binary / add numbers in base k: digit-wise addition with carries (see big integer arithmetic).
- Time formats and the factorial number system: mixed-radix conversions.