E (mathematical constant)

$e \approx 2.71828$ appears in several guises: the limit of $(1 + 1/n)^n$, the sum $\sum 1/k!$, the base whose exponential is its own derivative, and a number with a strikingly regular continued fraction. In Project Euler each guise shows up as a different kind of problem: an exact rational sequence (65), a calculus optimum (183), and a numerically delicate function (461). This page is about recognising which $e$ you are dealing with, because each one calls for a different tool.

The idea — four faces of one constant

face statement what it gives a solution
continued fraction $e = [2; 1, 2, 1, 1, 4, 1, 1, 6, \dots]$ exact rational convergents by recurrence
calculus $\frac{d}{dx}(x \ln(N/x)) = 0$ at $x = N/e$ where products like $(N/k)^k$ peak
series $e = \sum_{k \ge 0} 1/k!$, and $e^x = \sum x^k/k!$ closed forms for factorial-weighted sums
exponential $e^x - 1 \approx x$ for small $x$ the need for expm1 to avoid cancellation

The continued fraction is exceptional. Most irrationals have no pattern in their partial quotients, but $e$'s are $a_0 = 2$ and then the repeating triple $1, 2k, 1$ for $k = 1, 2, \dots$ (Euler, 1737). Its convergents therefore come from the usual recurrence $h_k = a_k h_{k-1} + h_{k-2}$ with an explicit $a_k$. The calculus face appears whenever a quantity is split into equal parts and then multiplied, because the maximiser of $(N/x)^x$ is $x = N/e$. The series face is behind any "expected number of uniform draws" or "sum of $1/i!$" problem. The probability that $n$ uniforms sum to less than 1 is $1/n!$, so the expected count of draws until their sum exceeds 1 is $\sum_{n \ge 0} 1/n! = e$.

Three faces across the problems

65, convergents of $e$: the continued fraction. The task is the numerator of the 100th convergent, then its digit sum. The partial quotients are generated by rule rather than computed from $e$, so no floating point is involved. The public Python encodes the rule (n is 1-indexed, so n == 1 is the leading 2):

def e_denominator(n: int) -> int:
    if n == 1:
        return 2
    elif n % 3 == 0:
        return 2 * n // 3
    else:
        return 1

The numerator grows roughly like $e^{n}$, so it needs arbitrary precision. See continued fraction for the convergent recurrence.

183, maximum product of parts: the calculus optimum. Splitting $N$ into $k$ equal parts maximises $(N/k)^k$ at $k = N/e$. The integer optimum is $\lfloor N/e \rfloor$ or $\lceil N/e \rceil$, and the solution compares the two in log space. The analysis is on concave function. Here $e$ is a real constant (math.e, M_E), and the only care needed is that comparing $k(\ln N - \ln k)$ in doubles picks the correct neighbour.

461, almost $\pi$: the exponential near zero. Four terms $f_n(k) = e^{k/n} - 1$ must sum as close to $\pi$ as possible, with $n = 10^4$. A meet-in-the-middle over sorted pair sums does the search. The $e$-specific point is numerical. For $k = 1$, $e^{0.0001} - 1$ computed as exp(x) - 1 subtracts two numbers near 1 and loses about four of its sixteen significant digits. The answer depends on matching $\pi$ to many digits, so the solution uses expm1, which computes $e^x - 1$ directly without the cancellation.

The unsolved problems on the list use the series face. 330 defines $a(n)$ by factorial-weighted sums whose values have the form $(A e + B)/n!$. 969 and 970 start from $H(1) = e$, the expected number of uniform hops needed to pass 1.

Python and C

  • 65: the two versions differ most here. Python evaluates the convergent top-down, recursing to depth $n$ and unwinding $a + 1/\text{rest}$ with exact Fractions. It raises sys.setrecursionlimit, and it carries numerators and denominators it never needs. C runs the numerator recurrence bottom-up over three rotating buffers of a hand-rolled base-$10^9$ BigInt. The decimal base makes the digit sum a direct read of each limb, and since limb * a_k + limb + carry stays below $2^{64}$, no wider type is needed.
  • 183: math.e and math.log against M_E and log. These are the same doubles, so the two languages pick the same neighbour of $N/e$.
  • 461: np.expm1 over an arange against a scalar expm1 loop. Both use the same libm routine, so the values match bit for bit. Python sorts the pair sums with np.argsort and finds the best complement with chunked searchsorted. C sorts an array of Pair structs with a hand-written introsort to avoid qsort's function-pointer calls over tens of millions of elements, then makes one two-pointer sweep.

How to reason about it

  • Identify the face before choosing the tool. The continued fraction needs exact big-integer recurrences, the calculus optimum needs a check of the two neighbouring integers, the series needs factorial sums, and the exponential near zero needs expm1.
  • Do not compute $e$ when its structure is known. 65 uses the partial-quotient rule and never needs $e$'s digits.
  • Expect $N/e$ when equal parts are multiplied. $(N/k)^k$, and products of equal shares in general, peak there.
  • Use expm1 and log1p near zero. $e^x - 1$ and $\ln(1 + x)$ for small $x$ lose digits when written naively.
  • For convergents, iterate rather than recurse. The bottom-up numerator recurrence needs no denominators and no recursion limit.

Problems3 / 6

  • ● 0065 — Convergents of $e$
  • ● 0183 — Maximum Product of Parts
  • ○ 0330 — Euler's Number
  • ● 0461 — Almost Pi
  • ○ 0969 — Kangaroo Hopping
  • ○ 0970 — Kangaroo Hopping over Sixes