All Euler problems
Project Euler

Searching a Triangular Array for a Sub-triangle Having the Minimum-sum

In a triangular array with 1000 rows, the entries are generated by a linear congruential generator: t_k = (615949 * t_(k-1) + 797807) mod 2^20, t_0 = 0 s_k = t_k - 2^19 Row r (0-indexed) has r+1 en...

Source sync Apr 19, 2026
Problem #0150
Level Level 07
Solved By 4,603
Languages C++, Python
Answer -271248680
Length 612 words
geometryoptimizationmodular_arithmetic

Problem Statement

This archive keeps the full statement, math, and original media on the page.

In a triangular array of positive and negative integers, we wish to find a sub-triangle such that the sum of the numbers it contains is the smallest possible.

In the example below, it can be easily verified that the marked triangle satisfies this condition having a sum of $−42$.

Problem illustration

We wish to make such a triangular array with one thousand rows, so we generate $500500$ pseudo-random numbers $s_k$ in the range $\pm 2^{19}$, using a type of random number generator (known as a Linear Congruential Generator) as follows:

t := 0;
  for k = 1 up to k = 500500 do
  begin
   t := (615949 * t + 797807) modulo 220; 
   sk := t - 219;
  end;

Thus: $s_1 = 273519$, $s_2 = -153582$, $s_3 = 450909$ etc

\[ \begin{array}{ccccccc} & & & 1 & & & \\ & & 1 & & 1 & & \\ & 1 & & 2 & & 1 & \\ 1 & & 3 & & 3 & & 1 \\ & & & \ldots & & & \\ \end{array} \]

Sub-triangles can start at any element of the array and extend down as far as we like (taking-in the two elements directly below it from the next row, the three elements directly below from the row after that, and so on).

The "sum of a sub-triangle" is defined as the sum of all the elements it contains.

Find the smallest possible sub-triangle sum.

Problem 150: Searching a Triangular Array for a Sub-triangle Having the Minimum-sum

Mathematical Analysis

Linear Congruential Generator

The LCG uses parameters a=615949a = 615949, c=797807c = 797807, modulus M=220=1048576M = 2^{20} = 1048576. The signed values sk=tk219s_k = t_k - 2^{19} range from 524288-524288 to 524287524287.

Lemma. The LCG is full-period since gcd(c,M)=1\gcd(c, M) = 1 (797807 is odd) and a1=615948a - 1 = 615948 is divisible by all prime factors of MM (namely 2), and 4 divides a1a - 1.

Prefix Sum Technique

Theorem. For each row rr, define the prefix sum P[r][j]=k=0j1s(r,k)P[r][j] = \sum_{k=0}^{j-1} s(r, k). Then the sum of entries from column cc to column c+ic+i in row rr is:

P[r][c+i+1]P[r][c]P[r][c+i+1] - P[r][c]

This allows each row-slice sum to be computed in O(1)O(1).

Sub-triangle Sum Recurrence

For a sub-triangle with apex (r,c)(r, c):

TriSum(r,c,h)=TriSum(r,c,h1)+(P[r+h][c+h+1]P[r+h][c])\text{TriSum}(r, c, h) = \text{TriSum}(r, c, h-1) + (P[r+h][c+h+1] - P[r+h][c])

By maintaining a running sum as hh increases, each new depth adds one O(1)O(1) row-slice lookup.

Complexity of Enumeration

The total number of (apex, depth) combinations is:

r=0N1c=0r(Nr)=r=0N1(r+1)(Nr)=N(N+1)(N+2)6\sum_{r=0}^{N-1} \sum_{c=0}^{r} (N - r) = \sum_{r=0}^{N-1} (r+1)(N-r) = \frac{N(N+1)(N+2)}{6}

For N=1000N = 1000: approximately 1.67×1081.67 \times 10^8 operations.

Why Negative Sums Exist

Since sk[219,2191]s_k \in [-2^{19}, 2^{19} - 1] with mean approximately 0.5-0.5, many sub-triangles have negative sums. The minimum-sum sub-triangle exploits regions of concentrated negative values.

Verification

  • Total number of entries: r=0999(r+1)=500500\sum_{r=0}^{999} (r+1) = 500500.
  • The minimum sub-triangle sum is negative and large in magnitude.

Solution Approaches

Approach 1: Prefix Sums + Triple Loop (Primary)

  1. Generate the triangular array using the LCG.
  2. Compute prefix sums for each row.
  3. For each apex (r,c)(r, c), extend depth h=0,1,h = 0, 1, \ldots while r+h<Nr + h < N.
  4. Track the running minimum over all sub-triangles.

Approach 2: Brute Force (for verification on small NN)

Generate a small triangle and enumerate all sub-triangles explicitly.

Proof of Correctness

The algorithm exhaustively examines every possible (apex, depth) pair. The prefix sum technique correctly computes each row-slice in O(1)O(1). The running minimum across all sub-triangles gives the global minimum.

Complexity Analysis

  • Time: O(N3/6)1.67×108O(N^3/6) \approx 1.67 \times 10^8 for N=1000N = 1000.
  • Space: O(N2/2)O(N^2/2) for the array and prefix sums.

Answer

271248680\boxed{-271248680}

Extended Analysis

Detailed Derivation

The solution proceeds through several key steps, each building on fundamental results from number theory and combinatorics.

Step 1: Problem Reduction. The original problem is first reduced to a computationally tractable form. This involves identifying the key mathematical structure (multiplicative functions, recurrences, generating functions, or geometric properties) that underlies the problem.

Step 2: Algorithm Design. Based on the mathematical structure, we design an efficient algorithm. The choice between dynamic programming, sieve methods, recursive enumeration, or numerical computation depends on the problem’s specific characteristics.

Step 3: Implementation. The algorithm is implemented with careful attention to numerical precision, overflow avoidance, and modular arithmetic where applicable.

Numerical Verification

The solution has been verified through multiple independent methods:

  1. Small-case brute force: For reduced problem sizes, exhaustive enumeration confirms the algorithm’s correctness.

  2. Cross-implementation: Both Python and C++ implementations produce identical results, ruling out language-specific numerical issues.

  3. Mathematical identities: Where applicable, the computed answer satisfies known mathematical identities or asymptotic bounds.

Historical Context

This problem draws on classical results in mathematics. The techniques used have roots in the work of Euler, Gauss, and other pioneers of number theory and combinatorics. Modern algorithmic implementations of these classical ideas enable computation at scales far beyond what was possible historically.

Error Analysis

For problems involving modular arithmetic, the computation is exact (no rounding errors). For problems involving floating-point computation, the algorithm maintains sufficient precision throughout to guarantee correctness of the final answer.

Alternative Approaches Considered

Several alternative approaches were considered during solution development:

  • Brute force enumeration: Feasible for verification on small inputs but exponential in the problem parameters, making it impractical for the full problem.

  • Analytic methods: Closed-form expressions or generating function techniques can sometimes bypass the need for explicit computation, but the problem’s structure may not always admit such simplification.

  • Probabilistic estimates: While useful for sanity-checking, these cannot provide the exact answer required.

Code

Each problem page includes the exact C++ and Python source files from the local archive.

C++ project_euler/problem_150/solution.cpp
#include <bits/stdc++.h>
using namespace std;

/*
 * Problem 150: Minimum-sum Sub-triangle
 * Generate triangular array via LCG, use prefix sums, enumerate all sub-triangles.
 * Complexity: O(N^3/6) ~ 1.67e8 for N=1000.
 */

int main() {
    const int N = 1000;
    const long long MOD = 1 << 20;

    // Generate triangular array
    vector<vector<long long>> tri(N);
    long long t = 0;
    for (int r = 0; r < N; r++) {
        tri[r].resize(r + 1);
        for (int c = 0; c <= r; c++) {
            t = (615949LL * t + 797807LL) % MOD;
            tri[r][c] = t - (1 << 19);
        }
    }

    // Prefix sums per row
    vector<vector<long long>> prefix(N);
    for (int r = 0; r < N; r++) {
        prefix[r].resize(r + 2, 0);
        for (int c = 0; c <= r; c++) {
            prefix[r][c + 1] = prefix[r][c] + tri[r][c];
        }
    }

    // Find minimum sub-triangle sum
    long long ans = LLONG_MAX;
    for (int r = 0; r < N; r++) {
        for (int c = 0; c <= r; c++) {
            long long triSum = 0;
            for (int h = 0; r + h < N; h++) {
                triSum += prefix[r + h][c + h + 1] - prefix[r + h][c];
                if (triSum < ans) ans = triSum;
            }
        }
    }

    assert(ans == -271248680LL);
    cout << ans << endl;
    return 0;
}