DEV Community

Gentrit Biba
Gentrit Biba

Posted on

Delving into Gaussian Integers: Cracking Project Euler Problem 153

Project Euler Problem 153 presents a fascinating challenge involving Gaussian Integers, complex numbers of the form a + bi where a and b are integers. The problem asks us to find the sum of all divisors with positive real parts for every integer up to a large limit (10^8). This requires a deep dive into number theory and clever optimization to arrive at an efficient solution.

Understanding the Problem

The core concept lies in identifying the divisors of a rational integer within the realm of Gaussian Integers. For instance, 5 has the following divisors with positive real parts: {1, 1 + 2i, 1 - 2i, 2 + i, 2 - i, 5}. The challenge is to compute the sum of these divisors for all integers up to 10^8.

Initial Approach and Optimization

A naive approach would involve iterating through all possible Gaussian Integers and checking for divisibility. However, this quickly becomes computationally infeasible for the given limit.

The optimized solution utilizes several key insights:

  • Exploiting Conjugates: If a + bi divides n, so does its conjugate a - bi. This allows us to consider only divisors with positive imaginary parts and double their contribution to the sum.
  • GCD Optimization: The greatest common divisor (GCD) is crucial. If gcd(a, b) != 1, the Gaussian Integer a + bi can be simplified, and its divisors are already accounted for by smaller integers.
  • Iterating Efficiently: Instead of brute-force checking, we iterate through possible values of a and b, ensuring a is always greater than or equal to b to avoid redundant calculations.
  • Mathematical Derivation: For a given a + bi, we can derive a formula to directly calculate the sum of its multiples that are less than or equal to the limit. This eliminates the need for further iterations.

Code Breakdown

Here's the JavaScript code that implements the optimized solution:

function customSeriesSum(a, b) {
  return a === b ? a + b : (a + b) * 2;
}

function gcd(a, b) {
  return !b ? a : gcd(b, a % b);
}

function calculateResult(limit) {
  let result = 0;
  const secondLimit = Math.floor(Math.sqrt(limit));

  for (let i = 1; i <= limit; i++) {
    result += Math.floor(limit / i) * i;
  }

  for (let real = 1; real <= secondLimit; real++) {
    for (let i = 1; i <= real; i++) {
      if (gcd(real, i) === 1) {
        const denominator = i * i + real * real;
        const value = customSeriesSum(real, i);
        let j = 1;
        while (denominator * j <= limit) {
          result += j * value * Math.floor(limit / (denominator * j));
          j++;
        }
      }
    }
  }

  return result;
}

const startTime = performance.now();
const limit = 10 ** 8;
const result = calculateResult(limit);
const endTime = performance.now();

console.log(result);
console.log(`Elapsed time: ${((endTime - startTime) / 1000).toFixed(2)} seconds`);
Enter fullscreen mode Exit fullscreen mode
  • customSeriesSum(a, b): This function efficiently calculates the sum of a + bi and a - bi, accounting for the case where a = b.
  • gcd(a, b): A standard recursive function to compute the GCD.
  • Main Loop: The code iterates through possible values of a and b, checks for gcd(a, b) = 1, calculates the denominator (a^2 + b^2), and then uses the derived formula to directly sum the contributions of the corresponding Gaussian Integer and its multiples.

Performance Considerations

Even with optimizations, the code deals with a large limit. The use of efficient mathematical formulas and GCD calculations significantly reduces the runtime, making the solution feasible. In fact, executing this code takes approximately 2.96 seconds.

This was fun! I write about stuff like this all the time on my blog. Swing by if you're into coding, math puzzles, and that kind of thing: blog.gentrit.dev

Top comments (0)