Open Source · 7 Languages · 900+ Problems

Project Euler Solutions

Complete solutions in C++, Python & Java — with step-by-step mathematical explanations

All Problems

Problem 831: Triple Product

View on Project Euler

Project Euler Problem 831 Solution

EulerSolve provides an optimized solution for Project Euler Problem 831, Triple Product, with C++, Python, Java, and a step-by-step mathematical explanation.

Problem Summary For the required exponent \(m=142857\), the quantity to compute is determined by the degree-5 coefficients of $Q(x)=1+3x+5x^2+5x^3+3x^4+x^5.$ If $Q(x)^m=\sum_{i\ge 0} q_i(m)x^i,$ then the target value is $T(m)=\sum_{i=0}^{5} q_i(m)\binom{5}{i}.$ The published output is the first ten digits of the base-7 expansion of \(T(142857)\). The challenge is that \(m\) is huge, so the method must avoid full polynomial expansion. Mathematical Approach The key observation is that only coefficients up to \(x^5\) ever matter. That allows the whole computation to happen inside a tiny truncated polynomial ring instead of on a gigantic full expansion. Step 1: Turn the weighted sum into one coefficient extraction Because $ (1+x)^5=\sum_{j=0}^{5}\binom{5}{j}x^j, $ multiplying \(Q(x)^m\) by \((1+x)^5\) shows that the required weighted sum is exactly the coefficient of \(x^5\): $T(m)=[x^5]\big(Q(x)^m(1+x)^5\big).$ This is the central reformulation. Instead of thinking about a separate post-processing step on six coefficients, we can now treat the whole problem as a single coefficient-extraction problem....

Detailed mathematical approach

Problem Summary

For the required exponent \(m=142857\), the quantity to compute is determined by the degree-5 coefficients of

$Q(x)=1+3x+5x^2+5x^3+3x^4+x^5.$

If

$Q(x)^m=\sum_{i\ge 0} q_i(m)x^i,$

then the target value is

$T(m)=\sum_{i=0}^{5} q_i(m)\binom{5}{i}.$

The published output is the first ten digits of the base-7 expansion of \(T(142857)\). The challenge is that \(m\) is huge, so the method must avoid full polynomial expansion.

Mathematical Approach

The key observation is that only coefficients up to \(x^5\) ever matter. That allows the whole computation to happen inside a tiny truncated polynomial ring instead of on a gigantic full expansion.

Step 1: Turn the weighted sum into one coefficient extraction

Because

$ (1+x)^5=\sum_{j=0}^{5}\binom{5}{j}x^j, $

multiplying \(Q(x)^m\) by \((1+x)^5\) shows that the required weighted sum is exactly the coefficient of \(x^5\):

$T(m)=[x^5]\big(Q(x)^m(1+x)^5\big).$

This is the central reformulation. Instead of thinking about a separate post-processing step on six coefficients, we can now treat the whole problem as a single coefficient-extraction problem.

Step 2: Factor the base polynomial

The degree-5 polynomial factors cleanly:

$Q(x)=1+3x+5x^2+5x^3+3x^4+x^5=(1+x)(1+x+x^2)^2.$

Substituting this into the previous identity gives

$T(m)=[x^5]\big((1+x)^{m+5}(1+x+x^2)^{2m}\big).$

This also makes the triple-product structure visible:

$T(m)=[x^5]\big((1+x)^5\cdot(1+x)^m\cdot(1+x+x^2)^m\cdot(1+x+x^2)^m\big).$

So the target comes from combining one linear factor and two identical trinomial factors, all under a degree-5 coefficient extraction.

Step 3: Only the first six coefficients are ever needed

Let two formal power series be equivalent modulo \(x^6\) when they agree through degree 5. Then

$A(x)\equiv \widetilde{A}(x)\pmod{x^6},\qquad B(x)\equiv \widetilde{B}(x)\pmod{x^6}$

implies

$A(x)B(x)\equiv \widetilde{A}(x)\widetilde{B}(x)\pmod{x^6}.$

That means every multiplication may discard all terms \(x^6,x^7,\dots\) immediately, because higher-degree terms can never later contribute back to the coefficient of \(x^5\). Therefore the whole computation takes place in the quotient ring

$\mathbb{Z}[x]/(x^6).$

In practical terms, each intermediate polynomial is represented by exactly six coefficients.

Step 4: Binary exponentiation on truncated polynomials

Write a truncated polynomial as

$a_0+a_1x+\cdots+a_5x^5.$

When multiplying two such polynomials, the coefficient of \(x^n\) for \(0\le n\le 5\) is the truncated convolution

$c_n=\sum_{i+j=n} a_i b_j.$

All terms with \(i+j>5\) are ignored. Since \(m=142857\) is large, the power \(Q(x)^m\) is computed by exponentiation by squaring: repeatedly square the current base, multiply it into the accumulator when the current binary digit of \(m\) is \(1\), and truncate after every multiplication. The number of polynomial multiplications is therefore \(O(\log m)\), not \(O(m)\).

Step 5: Equivalent coefficient formulas

The factorization also gives a closed coefficient expression. If

$a_r(m)=[x^r](1+x+x^2)^{2m},$

then

$T(m)=\sum_{r=0}^{5}\binom{m+5}{5-r}a_r(m).$

For fixed \(r\), the coefficient \(a_r(m)\) counts choices of \(r\) total degree from \(2m\) copies of \(1+x+x^2\). If exactly \(j\) factors contribute \(x^2\), then \(r-2j\) factors contribute \(x\), so

$a_r(m)=\sum_{j=0}^{\lfloor r/2\rfloor}\binom{2m}{j}\binom{2m-j}{r-2j}.$

This formula is mathematically useful, but the implementations do not need to evaluate it explicitly because truncated powering is simpler and already exact.

Worked Example

For \(m=1\),

$T(1)=[x^5]\big((1+x)^6(1+x+x^2)^2\big).$

Now

$ (1+x+x^2)^2 = 1+2x+3x^2+2x^3+x^4, $

and the needed coefficients of \((1+x)^6\) are

$1,6,15,20,15,6,\dots$

Therefore

$T(1)=6\cdot 1+15\cdot 2+20\cdot 3+15\cdot 2+6\cdot 1=132.$

The implementations also use a larger checkpoint at \(m=10\). Modulo \(x^6\),

$Q(x)^{10}\equiv 1+30x+455x^2+4640x^3+35715x^4+220906x^5,$

so

$T(10)=1+30\cdot 5+455\cdot 10+4640\cdot 10+35715\cdot 5+220906=450582.$

After scaling, this becomes

$7^{10}T(10)=127278262644918,$

which is exactly the consistency value checked by the implementation.

How the Code Works

The C++, Python, and Java implementations all follow the same structure. They store a truncated polynomial as a length-6 coefficient array, multiply two such arrays by convolution while discarding every term above degree 5, and use binary exponentiation to obtain the exact degree-5 truncation of \(Q(x)^{142857}\).

Once those six coefficients are known, the implementation forms the weighted sum with the sixth row of Pascal's triangle, namely \(1,5,10,10,5,1\). That produces the exact integer \(T(142857)\) without any floating-point approximation.

Finally, the implementation converts that integer to base 7 by repeated division and returns the first ten digits. One implementation also checks the intermediate value at exponent \(10\), confirming that the truncated arithmetic is behaving correctly before tackling the full exponent.

Complexity Analysis

Each truncated multiplication uses only coefficients of degrees \(0\) through \(5\), so it performs a constant amount of work: \(21\) coefficient products and the corresponding additions. Exponentiation by squaring needs \(O(\log m)\) such multiplications. Therefore the algorithm runs in \(O(\log m)\) big-integer operations and uses \(O(1)\) coefficient storage. The only growth comes from the size of the integers themselves, which must be large enough to hold the exact coefficients and the final base-7 conversion.

Footnotes and References

  1. Project Euler problem page: https://projecteuler.net/problem=831
  2. Generating functions: Wikipedia - Generating function
  3. Exponentiation by squaring: Wikipedia - Exponentiation by squaring
  4. Binomial theorem: Wikipedia - Binomial theorem
  5. Multinomial theorem: Wikipedia - Multinomial theorem

Mathematical approach · C++ solution · Python solution · Java solution

Previous: Problem 830 · All Project Euler solutions · Next: Problem 832

Need help with a problem? Ask me! 💡
e
✦ Euler GLM 5.2
Hi! I'm Euler. Ask me anything about Project Euler problems, math concepts, or solution approaches! 🧮