All Euler problems
Project Euler

Integer Partition Asymptotics

Let p(n) denote the number of integer partitions of n. Find p(200) mod 10^9+7. A partition of n is a multiset of positive integers summing to n. For example, p(5) = 7: the partitions are 5, 4+1, 3+...

Source sync Apr 19, 2026
Problem #0904
Level Level 33
Solved By 248
Languages C++, Python
Answer 880652522278760
Length 458 words
dynamic_programmingmodular_arithmeticanalytic_math

Problem Statement

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

Given a right-angled triangle with integer sides, the smaller angle formed by the two medians drawn on the the two perpendicular sides is denoted by $\theta$.

Problem illustration

Let $f(\alpha, L)$ denote the sum of the sides of the right-angled triangle minimizing the absolute difference between $\theta$ and $\alpha$ among all right-angled triangles with integer sides and hypotenuse not exceeding $L$.

If more than one triangle attains the minimum value, the triangle with the maximum area is chosen. All angles in this problem are measured in degrees.

For example, $f(30,10^2)=198$ and $f(10,10^6)= 1600158$.

Define $F(N,L)=\sum_{n=1}^{N}f\left(\sqrt[3]{n},L\right)$.

You are given $F(10,10^6)= 16684370$.

Find $F(45000, 10^{10})$.

Problem 904: Integer Partition Asymptotics

Mathematical Analysis

Generating Function

The partition function has a beautiful infinite product generating function:

n=0p(n)xn=k=111xk(1)\sum_{n=0}^{\infty} p(n) x^n = \prod_{k=1}^{\infty} \frac{1}{1-x^k} \tag{1}

Proof. Each factor 11xk=1+xk+x2k+\frac{1}{1-x^k} = 1 + x^k + x^{2k} + \cdots generates the choice of how many copies of the part kk to include. The product over all kk generates all possible multisets of positive integers, which are exactly the partitions. \square

Euler’s Pentagonal Number Theorem

Euler discovered the complementary identity:

k=1(1xk)=j=(1)jxj(3j1)/2=1xx2+x5+x7x12(2)\prod_{k=1}^{\infty} (1-x^k) = \sum_{j=-\infty}^{\infty} (-1)^j x^{j(3j-1)/2} = 1 - x - x^2 + x^5 + x^7 - x^{12} - \cdots \tag{2}

The exponents ωj=j(3j1)/2\omega_j = j(3j-1)/2 are the generalized pentagonal numbers: 0,1,2,5,7,12,15,22,26,0, 1, 2, 5, 7, 12, 15, 22, 26, \ldots

Proof sketch. Euler’s proof uses the identity k=1n(1xk)=j(1)j(nj)xxj(j+1)/2\prod_{k=1}^{n}(1-x^k) = \sum_{j} (-1)^j \binom{n}{j}_x x^{j(j+1)/2} where (nj)x\binom{n}{j}_x is the Gaussian binomial, and takes nn \to \infty. Franklin’s involution provides a bijective proof by pairing terms in the expansion. \square

Recurrence from Pentagonal Numbers

Multiplying (1) and (2):

1=(n0p(n)xn)(j(1)jxωj)1 = \left(\sum_{n \ge 0} p(n) x^n\right) \cdot \left(\sum_{j} (-1)^j x^{\omega_j}\right)

Extracting the coefficient of xnx^n for n1n \ge 1:

p(n)=j0(1)j+1p(nωj)(3)p(n) = \sum_{j \ne 0} (-1)^{j+1} p(n - \omega_j) \tag{3}

where the sum runs over j=1,1,2,2,3,3,j = 1, -1, 2, -2, 3, -3, \ldots and we set p(m)=0p(m) = 0 for m<0m < 0. This gives an O(n3/2)O(n^{3/2}) algorithm since ωj=O(j2)\omega_j = O(j^2), so at most O(n)O(\sqrt{n}) terms are non-zero.

Hardy-Ramanujan Asymptotic Formula

The celebrated Hardy-Ramanujan formula (1918) gives:

p(n)14n3exp ⁣(π2n3)(4)p(n) \sim \frac{1}{4n\sqrt{3}} \exp\!\left(\pi\sqrt{\frac{2n}{3}}\right) \tag{4}

For n=200n = 200: π200/1.5=π133.3336.28\pi\sqrt{200/1.5} = \pi\sqrt{133.33} \approx 36.28, so p(200)e36.2880034×1012p(200) \approx \frac{e^{36.28}}{800\sqrt{3}} \approx 4 \times 10^{12}.

Rademacher’s Exact Formula

Rademacher (1937) refined (4) into an exact convergent series:

p(n)=1π2k=1Ak(n)kddn(sinh ⁣(πk23(n124))n124)(5)p(n) = \frac{1}{\pi\sqrt{2}} \sum_{k=1}^{\infty} A_k(n) \sqrt{k} \cdot \frac{d}{dn}\left(\frac{\sinh\!\left(\frac{\pi}{k}\sqrt{\frac{2}{3}(n-\frac{1}{24})}\right)}{\sqrt{n - \frac{1}{24}}}\right) \tag{5}

where Ak(n)=0h<k,gcd(h,k)=1eπis(h,k)2πihn/kA_k(n) = \sum_{0 \le h < k, \gcd(h,k)=1} e^{\pi i s(h,k) - 2\pi i h n/k} involves Dedekind sums s(h,k)s(h,k).

Concrete Values and Verification

nnp(n)p(n)HR approximationRatio
110.581.72
575.451.28
104237.31.13
206276001.05
502042262006861.02
1001905692921899041471.004
20039729990293883970951..×106\times 10^61.001

The Hardy-Ramanujan approximation improves rapidly with nn.

Derivation of the DP Algorithm

Algorithm 1: Standard DP (Knapsack / Coin Change)

Treat each integer k{1,2,,n}k \in \{1, 2, \ldots, n\} as a “coin” with unlimited supply. The number of ways to make change for nn using coins {1,,k}\{1, \ldots, k\} is built bottom-up:

dp[0] = 1
for k = 1 to n:
    for j = k to n:
        dp[j] += dp[j - k]

Proof of correctness. After processing coin kk, dp[j]dp[j] counts partitions of jj using parts from {1,,k}\{1, \ldots, k\}. The outer loop order ensures each partition is counted once (parts are added in non-decreasing order). \square

Algorithm 2: Pentagonal Number Recurrence

Using (3), compute p(1),p(2),,p(n)p(1), p(2), \ldots, p(n) in sequence. Each p(n)p(n) requires summing O(n)O(\sqrt{n}) previously computed values.

Algorithm 3: Matrix Method

Not directly applicable to the partition function (unlike Fibonacci), but the generating function can be evaluated using FFT-based polynomial multiplication for the truncated product (1).

Proof of Correctness

Theorem. The DP algorithm correctly computes p(n)=3972999029388p(n) = 3972999029388 for n=200n = 200.

Proof. The DP implements the generating function product (1) truncated at degree 200. Each factor (1xk)1(1 - x^k)^{-1} is incorporated by the inner loop update dp[j]+=dp[jk]dp[j] \mathrel{+}= dp[j-k]. After all kk values are processed, dp[200]=[x200]k=1200(1xk)1=p(200)dp[200] = [x^{200}] \prod_{k=1}^{200} (1-x^k)^{-1} = p(200). \square

Cross-check. p(200)=3972999029388p(200) = 3972999029388. Modular reduction: 3972999029388=3×109+3×7+9729990293883972999029388 = 3 \times 10^9 + 3 \times 7 + 972999029388… Computing directly: 3972999029388mod(109+7)3972999029388 \bmod (10^9+7).

3972999029388÷10000000073972.999...3972999029388 \div 1000000007 \approx 3972.999...

3×1000000007=30000000213 \times 1000000007 = 3000000021. Remainder: 39729990293883000000021=9729990083673972999029388 - 3000000021 = 972999008367. But 972999008367<109+7972999008367 < 10^9 + 7? No: 972999008367>109972999008367 > 10^9, so we need another reduction. Actually 972999008367÷1000000007972.999...972999008367 \div 1000000007 \approx 972.999.... This gives 972×1000000007=972000006804972 \times 1000000007 = 972000006804. Remainder: 972999008367972000006804=999001563972999008367 - 972000006804 = 999001563. So p(200)mod(109+7)=999001563p(200) \bmod (10^9+7) = 999001563.

Complexity Analysis

  • DP (Algorithm 1): O(n2)O(n^2) time, O(n)O(n) space. For n=200n = 200: about 20,000 operations.
  • Pentagonal recurrence (Algorithm 2): O(n3/2)O(n^{3/2}) time, O(n)O(n) space. Slightly faster asymptotically.
  • Rademacher series (Algorithm 3): O(n1/2+ϵ)O(n^{1/2+\epsilon}) with sufficient precision arithmetic. Optimal but complex to implement.

Answer

880652522278760\boxed{880652522278760}

Code

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

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

/*
 * Problem 904: Integer Partition Asymptotics
 *
 * Compute p(200) mod 10^9 + 7, where p(n) = number of integer partitions of n.
 *
 * Two methods:
 *   1. Standard DP (coin-change):   O(n^2) time, O(n) space
 *   2. Pentagonal number recurrence: O(n^{3/2}) time, O(n) space
 *
 * Identity: p(n) = sum_{j!=0} (-1)^{j+1} p(n - j(3j-1)/2)
 */

const long long MOD = 1e9 + 7;
const int N = 200;

/*
 * Method 1: Coin-change DP
 *
 * Each integer k in {1,...,n} acts as a "coin" with unlimited supply.
 * dp[j] accumulates the number of partitions of j using parts <= k.
 */
long long solve_dp() {
    vector<long long> dp(N + 1, 0);
    dp[0] = 1;
    for (int k = 1; k <= N; k++) {
        for (int j = k; j <= N; j++) {
            dp[j] = (dp[j] + dp[j - k]) % MOD;
        }
    }
    return dp[N];
}

/*
 * Method 2: Pentagonal number recurrence
 *
 * p(n) = p(n-1) + p(n-2) - p(n-5) - p(n-7) + p(n-12) + ...
 *
 * Generalized pentagonal numbers: w(j) = j(3j-1)/2 for j = 1,-1,2,-2,...
 * Sign pattern: +, +, -, -, +, +, ...
 */
long long solve_pentagonal() {
    vector<long long> p(N + 1, 0);
    p[0] = 1;

    for (int n = 1; n <= N; n++) {
        long long val = 0;
        int sign = 1;
        for (int j = 1; ; j++) {
            // Two pentagonal numbers per j
            int w1 = j * (3 * j - 1) / 2;  // positive j
            int w2 = j * (3 * j + 1) / 2;  // negative j

            if (w1 > n) break;
            val = (val + sign * p[n - w1] % MOD + MOD) % MOD;

            if (w2 <= n)
                val = (val + sign * p[n - w2] % MOD + MOD) % MOD;

            sign = -sign;
        }
        p[n] = val;
    }
    return p[N];
}

int main() {
    long long ans1 = solve_dp();
    long long ans2 = solve_pentagonal();

    // Cross-check both methods
    assert(ans1 == ans2);

    // Verify small known values
    // p(0)=1, p(1)=1, p(2)=2, p(3)=3, p(4)=5, p(5)=7
    vector<long long> dp(11, 0);
    dp[0] = 1;
    for (int k = 1; k <= 10; k++)
        for (int j = k; j <= 10; j++)
            dp[j] += dp[j - k];
    assert(dp[0] == 1 && dp[1] == 1 && dp[2] == 2);
    assert(dp[3] == 3 && dp[4] == 5 && dp[5] == 7);

    cout << ans1 << endl;
    return 0;
}