Project Euler Problem 169
Define f(0)=1 and f(n) to be the number of different ways n can be expressed as a sum of integer powers of 2 using each
Solution
Answer: 178653872807
We must compute the number of representations of
$$10^{25}$$
as a sum of powers of $2$, where each power may be used at most twice.
Define
$$f(n)=#\left{n=\sum_{k\ge0} a_k2^k ;\middle|; a_k\in{0,1,2}\right}.$$
We are asked for $f(10^{25})$.
Mathematical analysis
The problem is fundamentally about binary carries.
Suppose
$$n=\sum_{k\ge0} b_k2^k$$
is the ordinary binary expansion of $n$, where $b_k\in{0,1}$.
We seek the number of digit sequences
$$a_k\in{0,1,2}$$
such that
$$\sum a_k2^k=n.$$
The key observation is that the only interaction between adjacent bits comes from carries.
Carry-based dynamic programming
Process bits from least significant to most significant.
At bit position $k$:
- the target bit is $b_k\in{0,1}$,
- we choose a coefficient $a_k\in{0,1,2}$,
- we may also receive a carry $c_k$ from the previous bit.
We require
$$a_k + c_k \equiv b_k \pmod 2.$$
The next carry is
$$c_{k+1}=\frac{a_k+c_k-b_k}{2}.$$
Because $a_k\le2$ and carries stay tiny, the state space is extremely small.
Thus we can compute $f(n)$ with a digit DP.
Example: $f(10)$
Binary:
$$10 = 1010_2.$$
Running the carry recursion yields exactly 5 representations:
$$\begin{aligned} 10 &= 8+2\ &=8+1+1\ &=4+4+2\ &=4+4+1+1\ &=4+2+2+1+1. \end{aligned}$$
So $f(10)=5$, matching the statement.
Python implementation
from functools import lru_cache
def f(n):
# Binary digits of n
bits = list(map(int, bin(n)[2:]))
@lru_cache(None)
def dp(pos, carry):
"""
pos : current bit position (from least significant upward)
carry : incoming carry at this bit
Returns the number of valid completions.
"""
# All bits processed
if pos == len(bits):
return 1 if carry == 0 else 0
# Current target bit of n
bit = bits[-1 - pos]
total = 0
# Choose coefficient a_k in {0,1,2}
for digit in (0, 1, 2):
s = digit + carry
# Parity must match the target bit
if s % 2 == bit:
# Compute next carry
next_carry = (s - bit) // 2
total += dp(pos + 1, next_carry)
return total
return dp(0, 0)
# Problem value
print(f(10**25))
Code walkthrough
Convert to binary
bits = list(map(int, bin(n)[2:]))
This stores the binary digits of $n$.
For example:
$$10 \to [1,0,1,0].$$
Memoized recursion
@lru_cache(None)
def dp(pos, carry):
State:
pos= current bit index,carry= carry entering this bit.
The recursion counts all valid digit choices from this point onward.
Base case
if pos == len(bits):
return 1 if carry == 0 else 0
After all bits are processed, we succeed only if no carry remains.
Trying coefficients $0,1,2$
for digit in (0, 1, 2):
Each power of two may appear:
- 0 times,
- 1 time,
- or 2 times.
Bit parity condition
if s % 2 == bit:
where
s = digit + carry
The current bit must match the binary digit of $n$.
Next carry
next_carry = (s - bit) // 2
This is ordinary binary carrying.
Verification on the small example
Running:
print(f(10))
produces:
5
which matches the problem statement exactly.
Final computation
Evaluating the program for
$$10^{25}$$
gives:
$$f(10^{25}) = 178653872807.$$
This magnitude is reasonable: the number of representations grows roughly exponentially in the number of binary digits, and $10^{25}$ has only about 84 bits, so a result around $10^{11}$ is plausible.
Answer: 178653872807