Open Source · 7 Languages · 900+ Problems

Project Euler Solutions

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

All Problems

Problem 515: Dissonant Numbers

View on Project Euler

Project Euler Problem 515 Solution

EulerSolve provides an optimized solution for Project Euler Problem 515, Dissonant Numbers, with C++, Python, Java, and a step-by-step mathematical explanation.

Problem Summary We must evaluate the prime sum $D(a,b,k)=\sum_{\substack{a \le p \lt a+b\\ p\ \text{prime}}} d(p,p-1,k).$ The key fact used by the implementation is that for every contributing prime \(p\), the required term collapses to a modular inverse of \(k-1\) modulo \(p\). After that simplification, the whole problem becomes: enumerate the primes in a long interval efficiently, compute one inverse for each of them, and add the results. Mathematical Approach The number-theoretic core is simple once the prime-term identity is recognized. The difficult part is not symbolic algebra, but organizing the computation so that the interval \([a,a+b)\) can be processed without sieving every number up to \(a+b\). Step 1: Reduce each term to a congruence The implementation uses the identity $d(p,p-1,k)\equiv (k-1)^{-1}\pmod{p}.$ So for each prime \(p\) in the interval, the required contribution is the unique residue \(x\) satisfying $(k-1)x \equiv 1 \pmod{p}.$ Because the modulus is prime, this inverse exists whenever \(p \nmid (k-1)\). In other words, the original arithmetic object \(d(p,p-1,k)\) does not need to be built directly; it is enough to solve one linear congruence per prime....

Detailed mathematical approach

Problem Summary

We must evaluate the prime sum

$D(a,b,k)=\sum_{\substack{a \le p \lt a+b\\ p\ \text{prime}}} d(p,p-1,k).$

The key fact used by the implementation is that for every contributing prime \(p\), the required term collapses to a modular inverse of \(k-1\) modulo \(p\). After that simplification, the whole problem becomes: enumerate the primes in a long interval efficiently, compute one inverse for each of them, and add the results.

Mathematical Approach

The number-theoretic core is simple once the prime-term identity is recognized. The difficult part is not symbolic algebra, but organizing the computation so that the interval \([a,a+b)\) can be processed without sieving every number up to \(a+b\).

Step 1: Reduce each term to a congruence

The implementation uses the identity

$d(p,p-1,k)\equiv (k-1)^{-1}\pmod{p}.$

So for each prime \(p\) in the interval, the required contribution is the unique residue \(x\) satisfying

$(k-1)x \equiv 1 \pmod{p}.$

Because the modulus is prime, this inverse exists whenever \(p \nmid (k-1)\). In other words, the original arithmetic object \(d(p,p-1,k)\) does not need to be built directly; it is enough to solve one linear congruence per prime.

Step 2: Use Bézout coefficients to obtain the inverse

When \(\gcd(k-1,p)=1\), the extended Euclidean algorithm finds integers \(u\) and \(v\) such that

$u(k-1)+vp=1.$

Reducing that identity modulo \(p\) removes the second term and leaves

$u(k-1)\equiv 1 \pmod{p}.$

Therefore the required contribution is simply the least nonnegative residue of \(u\):

$d(p,p-1,k)=u \bmod p.$

This is why the three implementations use the extended Euclidean algorithm rather than repeated powering. It produces the inverse directly and works uniformly in all three languages.

Step 3: Enumerate primes with a segmented sieve

The interval can start near \(10^9\), so ordinary sieving from \(2\) all the way to \(a+b\) would be wasteful. Instead, let

$R=a+b-1,\qquad s=\left\lfloor \sqrt{R}\right\rfloor.$

First compute all primes up to \(s\) with a standard sieve. These are the only base primes needed to mark composites in the target interval. For each base prime \(q\), the first multiple that must be removed from \([a,a+b)\) is

$m_q=\max\left(q^2,\left\lceil\frac{a}{q}\right\rceil q\right).$

Then every number

$m_q,\ m_q+q,\ m_q+2q,\dots$

inside the interval is composite and can be marked. After all base primes are processed, the unmarked entries are exactly the primes in \([a,a+b)\).

Step 4: Accumulate the prime contributions

Once the interval primes are known, the sum is evaluated term by term:

$D(a,b,k)=\sum_{\substack{a \le p \lt a+b\\ p\ \text{prime}}} \left((k-1)^{-1} \bmod p\right).$

No further combinatorial structure is hidden here. The algorithm does precisely two things: identify the primes in the interval and attach to each prime the modular inverse of \(k-1\).

Worked Example: \(D(101,1,10)=45\)

The interval \([101,102)\) contains only one prime, namely \(p=101\). Here

$k-1=9.$

So we solve

$9x \equiv 1 \pmod{101}.$

Since

$9\cdot 45 = 405 = 4\cdot 101 + 1,$

the inverse of \(9\) modulo \(101\) is \(45\). Therefore

$D(101,1,10)=45,$

which matches the checkpoint used by the implementation.

How the Code Works

The C++, Python, and Java implementations follow the same pipeline. They first sieve all primes up to \(\lfloor\sqrt{a+b-1}\rfloor\). Next they allocate a Boolean segment of length \(b\) representing the numbers \(a,a+1,\dots,a+b-1\), and every composite hit by a base prime is marked. After that scan, each unmarked position corresponds to a prime \(p\) in the interval.

For every such prime, the implementation runs the extended Euclidean algorithm on \(k-1\) and \(p\), normalizes the Bézout coefficient into the range \(0\) to \(p-1\), and adds that value to the running total. The inverse routine is written defensively: if the reduced value of \(k-1\) is \(0\) modulo \(p\), or if the gcd is not \(1\), it returns \(0\). For the intended evaluations, the contributing primes are coprime to \(k-1\), so each genuine term is well defined.

Complexity Analysis

Let \(R=a+b-1\). Generating all base primes up to \(\lfloor\sqrt{R}\rfloor\) costs \(O(\sqrt{R}\log\log R)\) time and \(O(\sqrt{R})\) memory with the simple sieve used in the implementations. Marking the interval of length \(b\) costs \(O(b\log\log R)\) time and \(O(b)\) memory. If \(\pi([a,a+b))\) denotes the number of primes in the interval, the inverse computations add \(O(\pi([a,a+b))\log R)\) time. So the total memory usage is linear in the interval length, and the method is efficient because it never stores all integers up to \(a+b\).

Footnotes and References

  1. Problem page: https://projecteuler.net/problem=515
  2. Modular multiplicative inverse: Wikipedia — Modular multiplicative inverse
  3. Extended Euclidean algorithm: Wikipedia — Extended Euclidean algorithm
  4. Bézout's identity: Wikipedia — Bézout's identity
  5. Segmented sieve: Wikipedia — Segmented sieve

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

Previous: Problem 514 · All Project Euler solutions · Next: Problem 516

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! 🧮