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

Project Euler Problem 169

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