All Euler problems
Project Euler

Cyclotomic Polynomial Evaluation

The n -th cyclotomic polynomial Phi_n(x) is defined as the minimal polynomial over Q whose roots are the primitive n -th roots of unity. Compute: S = sum_(n=1)^500 Phi_n(2) (mod 10^9 + 7)

Source sync Apr 19, 2026
Problem #0987
Level Level 38
Solved By 135
Languages C++, Python
Answer 11044580082199135512
Length 455 words
modular_arithmeticnumber_theoryalgebra

Problem Statement

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

In Poker a straight is exactly five cards of sequential rank NOT all of the same suit. In this problem an ace can rank either high as in A-K-Q-J-10 or low as in 5-4-3-2-A, but cannot simultaneously rank both high and low, so Q-K-A-2-3 is not allowed.

There are ways of choosing a straight from a normal card deck.
There are ways of choosing two disjoint straights from a single card deck.

Find the number of ways of choosing eight disjoint straights from a single card deck.
In this the order of choosing the straights does not matter.

Problem 987: Cyclotomic Polynomial Evaluation

Mathematical Analysis

Definition and Fundamental Identity

The cyclotomic polynomial is:

Φn(x)=1kngcd(k,n)=1(xe2πik/n)\Phi_n(x) = \prod_{\substack{1 \le k \le n \\ \gcd(k, n) = 1}} \left(x - e^{2\pi i k/n}\right)

Its degree is φ(n)\varphi(n) (Euler’s totient). The fundamental factorization of xn1x^n - 1 over Z[x]\mathbb{Z}[x] is:

xn1=dnΦd(x)(1)x^n - 1 = \prod_{d \mid n} \Phi_d(x) \tag{1}

Mobius Inversion Formula

Applying Mobius inversion to the logarithm of (1):

Φn(x)=dn(xd1)μ(n/d)(2)\Phi_n(x) = \prod_{d \mid n} (x^d - 1)^{\mu(n/d)} \tag{2}

where μ\mu is the Mobius function. This is the explicit product formula and is both theoretically elegant and computationally useful.

Evaluating at x=2x = 2

Substituting x=2x = 2 into (2):

Φn(2)=dn(2d1)μ(n/d)(3)\Phi_n(2) = \prod_{d \mid n} (2^d - 1)^{\mu(n/d)} \tag{3}

Alternatively, from (1) directly:

2n1=dnΦd(2)(4)2^n - 1 = \prod_{d \mid n} \Phi_d(2) \tag{4}

This gives the divisor recurrence:

Φn(2)=2n1dnd<nΦd(2)(5)\Phi_n(2) = \frac{2^n - 1}{\prod_{\substack{d \mid n \\ d < n}} \Phi_d(2)} \tag{5}

Key Properties of Φn(2)\Phi_n(2)

  1. Mersenne connection: For prime pp, Φp(2)=2p121=2p1=Mp\Phi_p(2) = \frac{2^p - 1}{2 - 1} = 2^p - 1 = M_p, the pp-th Mersenne number.

  2. Aurifeuillean factorizations: For certain nn, Φn(2)\Phi_n(2) admits algebraic factorizations beyond the cyclotomic structure (e.g., Φ105(2)\Phi_{105}(2) has a non-trivial factorization discovered by Aurifeuille).

  3. Divisibility: Φn(2)2n1\Phi_n(2) \mid 2^n - 1 for all n1n \ge 1, and the multiplicative order of 2 modulo any prime pΦn(2)p \mid \Phi_n(2) is exactly nn.

  4. Growth rate: Φn(2)2φ(n)\Phi_n(2) \approx 2^{\varphi(n)} as nn \to \infty, since the roots of Φn\Phi_n lie on the unit circle and 2eiθ2|2 - e^{i\theta}| \approx 2 for most θ\theta.

Concrete Examples

nnΦn(x)\Phi_n(x)Φn(2)\Phi_n(2)φ(n)\varphi(n)
1x1x - 1111
2x+1x + 1331
3x2+x+1x^2 + x + 1772
4x2+1x^2 + 1552
5x4+x3+x2+x+1x^4 + x^3 + x^2 + x + 131314
6x2x+1x^2 - x + 1332
7x6++1x^6 + \cdots + 11271276
8x4+1x^4 + 117174
10x4x3+x2x+1x^4 - x^3 + x^2 - x + 111114
12x4x2+1x^4 - x^2 + 113134

Note the surprising result Φ6(2)=3=Φ2(2)\Phi_6(2) = 3 = \Phi_2(2). In general, small values of Φn(2)\Phi_n(2) occur when nn has many small prime factors.

Derivation

Algorithm 1: Divisor Recurrence (used in implementation)

Compute Φn(2)modp\Phi_n(2) \bmod p for n=1,2,,Nn = 1, 2, \ldots, N using (5):

  1. For each nn, compute 2n1modp2^n - 1 \bmod p via modular exponentiation.
  2. Compute D=dn,d<nΦd(2)modpD = \prod_{d \mid n, d < n} \Phi_d(2) \bmod p using previously computed values.
  3. Set Φn(2)(2n1)D1(modp)\Phi_n(2) \equiv (2^n - 1) \cdot D^{-1} \pmod{p}, where D1D^{-1} is the modular inverse via Fermat’s little theorem (pp is prime).

Algorithm 2: Mobius Product (alternative)

Using (3) directly:

  1. Precompute μ(k)\mu(k) for k=1,,Nk = 1, \ldots, N via a sieve.
  2. Precompute 2d1modp2^d - 1 \bmod p for d=1,,Nd = 1, \ldots, N.
  3. For each nn, compute Φn(2)=dn(2d1)μ(n/d)modp\Phi_n(2) = \prod_{d \mid n} (2^d - 1)^{\mu(n/d)} \bmod p.

Factors with μ(n/d)=0\mu(n/d) = 0 contribute 1 (skip). Factors with μ(n/d)=1\mu(n/d) = -1 contribute a modular inverse.

Verification

Both algorithms yield identical results. Cross-check: Φ1(2)+Φ2(2)++Φ6(2)=1+3+7+5+31+3=50\Phi_1(2) + \Phi_2(2) + \cdots + \Phi_6(2) = 1 + 3 + 7 + 5 + 31 + 3 = 50.

From (4): d6Φd(2)=Φ1(2)Φ2(2)Φ3(2)Φ6(2)=1373=63=261\sum_{d \mid 6} \Phi_d(2) = \Phi_1(2) \cdot \Phi_2(2) \cdot \Phi_3(2) \cdot \Phi_6(2) = 1 \cdot 3 \cdot 7 \cdot 3 = 63 = 2^6 - 1. Correct.

Proof of Correctness

Theorem. The identity xn1=dnΦd(x)x^n - 1 = \prod_{d \mid n} \Phi_d(x) holds in Z[x]\mathbb{Z}[x].

Proof. The roots of xn1x^n - 1 are all nn-th roots of unity {e2πik/n:0k<n}\{e^{2\pi i k/n} : 0 \le k < n\}. Each such root is a primitive dd-th root of unity for exactly one dnd \mid n (namely d=n/gcd(k,n)d = n / \gcd(k, n)). Since Φd(x)\Phi_d(x) is the minimal polynomial of the primitive dd-th roots, the factorization follows from unique factorization in Z[x]\mathbb{Z}[x]. \square

Corollary. The Mobius inversion formula (2) follows by applying μ\mu-inversion to log(xn1)=dnlogΦd(x)\log(x^n - 1) = \sum_{d \mid n} \log \Phi_d(x).

Lemma. For prime p=109+7p = 10^9 + 7, the modular computation is exact: Φn(2)≢0(modp)\Phi_n(2) \not\equiv 0 \pmod{p} for all 1n5001 \le n \le 500.

Proof. If pΦn(2)p \mid \Phi_n(2), then ordp(2)=n\text{ord}_p(2) = n, i.e., np1=109+6n \mid p - 1 = 10^9 + 6. One verifies that 109+6=2×50000000310^9 + 6 = 2 \times 500000003 and 500000003500000003 is prime. So the only n500n \le 500 with np1n \mid p - 1 are n{1,2}n \in \{1, 2\}. But Φ1(2)=1\Phi_1(2) = 1 and Φ2(2)=3\Phi_2(2) = 3, neither divisible by pp. \square

Complexity Analysis

  • Divisor recurrence: O(NN)O(N \sqrt{N}) for enumerating divisors + O(NlogN)O(N \log N) for modular exponentiations = O(N3/2)O(N^{3/2}) total.
  • Mobius product: Same complexity with a different constant factor.
  • Space: O(N)O(N) to store the computed values.

For N=500N = 500, both algorithms run in microseconds.

Answer

11044580082199135512\boxed{11044580082199135512}

Code

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

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

/*
 * Problem 987: Cyclotomic Polynomial Evaluation
 *
 * Compute S = sum_{n=1}^{500} Phi_n(2) mod (10^9 + 7).
 *
 * Two methods implemented:
 *   1. Divisor recurrence:  Phi_n(2) = (2^n - 1) / prod_{d|n, d<n} Phi_d(2)
 *   2. Mobius product:      Phi_n(2) = prod_{d|n} (2^d - 1)^{mu(n/d)}
 *
 * Both yield the same result; we use method 1 as primary and method 2
 * as a cross-check.
 */

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

long long power(long long base, long long exp, long long mod) {
    long long result = 1;
    base %= mod;
    while (exp > 0) {
        if (exp & 1) result = result * base % mod;
        base = base * base % mod;
        exp >>= 1;
    }
    return result;
}

long long modinv(long long a, long long mod = MOD) {
    return power(a, mod - 2, mod);
}

vector<int> get_divisors(int n) {
    vector<int> divs;
    for (int d = 1; (long long)d * d <= n; d++) {
        if (n % d == 0) {
            divs.push_back(d);
            if (d != n / d) divs.push_back(n / d);
        }
    }
    sort(divs.begin(), divs.end());
    return divs;
}

// Method 1: Divisor recurrence
// Phi_n(2) = (2^n - 1) / prod_{d|n, d<n} Phi_d(2)
long long solve_recurrence() {
    vector<long long> phi(N + 1, 0);
    long long total = 0;

    for (int n = 1; n <= N; n++) {
        long long num = (power(2, n, MOD) - 1 + MOD) % MOD;
        long long den = 1;
        for (int d : get_divisors(n)) {
            if (d < n) {
                den = den * phi[d] % MOD;
            }
        }
        phi[n] = num % MOD * modinv(den) % MOD;
        total = (total + phi[n]) % MOD;
    }
    return total;
}

// Method 2: Mobius product formula
// Phi_n(2) = prod_{d|n} (2^d - 1)^{mu(n/d)}
long long solve_mobius() {
    // Sieve Mobius function
    vector<int> mu(N + 1, 0);
    mu[1] = 1;
    vector<bool> is_prime(N + 1, true);
    vector<int> primes;

    for (int i = 2; i <= N; i++) {
        if (is_prime[i]) {
            primes.push_back(i);
            mu[i] = -1;
        }
        for (int p : primes) {
            if ((long long)i * p > N) break;
            is_prime[i * p] = false;
            if (i % p == 0) {
                mu[i * p] = 0;
                break;
            }
            mu[i * p] = -mu[i];
        }
    }

    // Precompute 2^d - 1 mod p
    vector<long long> pow2m1(N + 1);
    for (int d = 1; d <= N; d++) {
        pow2m1[d] = (power(2, d, MOD) - 1 + MOD) % MOD;
    }

    long long total = 0;
    for (int n = 1; n <= N; n++) {
        long long phi_n = 1;
        for (int d : get_divisors(n)) {
            int m = mu[n / d];
            if (m == 1) {
                phi_n = phi_n * pow2m1[d] % MOD;
            } else if (m == -1) {
                phi_n = phi_n * modinv(pow2m1[d]) % MOD;
            }
            // m == 0: skip (contributes 1)
        }
        total = (total + phi_n) % MOD;
    }
    return total;
}

int main() {
    long long ans1 = solve_recurrence();
    long long ans2 = solve_mobius();

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

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