Project Euler Problem 399: Squarefree Fibonacci Numbers
The first 15 Fibonacci numbers are: 1, 1, 2, 3, 5, 8, 13, 21, 34, 55, 89, 144, 233, 377, 610. A Fibonacci number F(n) is squarefree if it is not divisible by any perfect square other than 1. Among...
Problem Statement
This archive keeps the full statement, math, and original media on the page.
The first \(15\) Fibonacci numbers are:
\(1,1,2,3,5,8,13,21,34,55,89,144,233,377,610\).
It can be seen that \(8\) and \(144\) are not squarefree: \(8\) is divisible by \(4\) and \(144\) is divisible by \(4\) and by \(9\).
So the first \(13\) squarefree Fibonacci numbers are:
\(1,1,2,3,5,13,21,34,55,89,233,377\) and \(610\).
The \(200\)th squarefree Fibonacci number is:
\(971183874599339129547649988289594072811608739584170445\).
The last sixteen digits of this number are: \(1608739584170445\) and in scientific notation this number can be written as \(9.7\mathrm e53\).
Find the \(100\,000\,000\)th squarefree Fibonacci number.
Give as your answer its last sixteen digits followed by a comma followed by the number in scientific notation (rounded to one digit after the decimal point).
For the \(200\)th squarefree number the answer would have been: 1608739584170445,9.7e53
Project Euler Problem 399: Squarefree Fibonacci Numbers
Key Mathematical Concepts
Rank of Apparition (Entry Point)
For a prime p, the rank of apparition alpha(p) is the smallest positive index k such that p | F(k). Key properties:
- p | F(n) if and only if alpha(p) | n
- alpha(2) = 3, alpha(3) = 4, alpha(5) = 5, alpha(7) = 8, …
- alpha(p) divides p - 1 if p = 1 or 4 (mod 5), and divides 2(p + 1) if p = 2 or 3 (mod 5)
Wall’s Conjecture
Wall’s conjecture (assumed true for this problem): For every prime p, p^2 does not divide F(alpha(p)). Equivalently, alpha(p^2) = p * alpha(p).
This has been verified for all primes up to 3 * 10^15, but remains unproven in general.
When is F(n) NOT squarefree?
Under Wall’s conjecture:
- p^2 | F(n) if and only if p * alpha(p) | n
- F(n) is NOT squarefree if and only if there exists some prime p such that p * alpha(p) | n
Density of Squarefree Fibonacci Numbers
The proportion of squarefree Fibonacci numbers is:
Product over all primes p of (1 - 1/(p * alpha(p)))
This product converges to approximately 0.7647, which is higher than the density of squarefree integers (6/pi^2 ~ 0.6079).
Editorial
The largest prime we need to consider: if p * alpha(p) <= N, we need to include p. Since alpha(p) >= 1, we need primes up to N. But alpha(p) grows, so in practice we only need primes up to about sqrt(N) or a bit more (since alpha(p) >= p-1 for some primes, p * alpha(p) grows fast). Actually alpha(p) can be as small as (p-1)/2 for some primes, so we need primes up to roughly sqrt(2*N). Once we find the index k of the 10^8-th squarefree Fibonacci number. We generate primes up to a bound B using a sieve of Eratosthenes. We then iterate over each prime p, compute alpha(p) by finding the Fibonacci sequence mod p until hitting 0. Finally, in a boolean sieve array, mark all multiples of q(p) as “non-squarefree”.
Pseudocode
Generate primes up to a bound B using a sieve of Eratosthenes
For each prime p, compute alpha(p) by finding the Fibonacci sequence mod p until hitting 0
Compute q(p) = p * alpha(p)
In a boolean sieve array, mark all multiples of q(p) as "non-squarefree"
Compute F(k) mod 10^16 using matrix exponentiation or the doubling method
Compute log10(F(k)) = k * log10(phi) - 0.5 * log10(5) for large k (Binet's formula approximation)
Format the output
Correctness
Theorem. The method described above computes exactly the quantity requested in the problem statement.
Proof. The preceding analysis identifies the admissible objects and derives the formula, recurrence, or exhaustive search carried out by the algorithm. The computation evaluates exactly that specification, so every valid contribution is included once and no invalid contribution is counted. Therefore the returned value is the required answer.
Complexity
- Sieve of Eratosthenes: O(N log log N)
- Computing alpha(p) for each prime: O(alpha(p)) per prime, total O(N) across all primes
- Marking multiples: O(N/q(p)) per prime, total O(N * sum(1/q(p))) = O(N log log N)
- Fast Fibonacci: O(log k) with k ~ 1.3 * 10^8
- Total: O(N log log N) time, O(N) space
References
Answer
Code
Each problem page includes the exact C++ and Python source files from the local archive.
#include <bits/stdc++.h>
using namespace std;
// Sieve of Eratosthenes
vector<int> sieve_primes(int limit) {
vector<bool> is_prime(limit + 1, true);
is_prime[0] = is_prime[1] = false;
for (int i = 2; (long long)i * i <= limit; i++) {
if (is_prime[i]) {
for (int j = i * i; j <= limit; j += i)
is_prime[j] = false;
}
}
vector<int> primes;
for (int i = 2; i <= limit; i++)
if (is_prime[i]) primes.push_back(i);
return primes;
}
// Rank of apparition: smallest k > 0 with F(k) = 0 (mod p)
int rank_of_apparition(int p) {
long long a = 0, b = 1;
for (int k = 1; k <= 6 * p + 10; k++) {
long long c = (a + b) % p;
a = b;
b = c;
if (a == 0) return k;
}
return -1; // Should not happen
}
// Modular multiplication avoiding overflow for 64-bit
// For mod up to 10^16, we need 128-bit multiplication
typedef unsigned long long ull;
typedef __int128 lll;
ull mulmod(ull a, ull b, ull mod) {
return (lll)a * b % mod;
}
ull addmod(ull a, ull b, ull mod) {
return (a + b) % mod;
}
// Compute (F(n), F(n+1)) mod m using the doubling method
pair<ull, ull> fib_mod(long long n, ull mod) {
if (n == 0) return {0, 1};
auto [a, b] = fib_mod(n >> 1, mod);
// F(2k) = F(k) * (2*F(k+1) - F(k))
// F(2k+1) = F(k)^2 + F(k+1)^2
ull c = mulmod(a, (2 * b % mod - a + mod) % mod, mod);
ull d = addmod(mulmod(a, a, mod), mulmod(b, b, mod), mod);
if (n & 1)
return {d, addmod(c, d, mod)};
else
return {c, d};
}
int main() {
ios_base::sync_with_stdio(false);
cin.tie(NULL);
const int TARGET = 100000000; // 10^8
// Estimate sieve size: ~76.47% of Fibonacci numbers are squarefree
const int N = (int)(TARGET / 0.76) + 5000;
printf("Sieve size N = %d\n", N);
printf("Target: %d-th squarefree Fibonacci number\n", TARGET);
// Step 1: Generate primes up to N/2
int prime_limit = N / 2 + 1;
printf("Generating primes up to %d...\n", prime_limit);
vector<int> primes = sieve_primes(prime_limit);
printf("Found %zu primes\n", primes.size());
// Step 2: Sieve non-squarefree indices
vector<bool> not_sqfree(N + 1, false);
printf("Computing ranks of apparition and sieving...\n");
int primes_used = 0;
for (int p : primes) {
int alpha = rank_of_apparition(p);
long long q = (long long)p * alpha;
if (q > N) continue;
primes_used++;
for (long long j = q; j <= N; j += q)
not_sqfree[j] = true;
}
printf("Primes contributing to sieve: %d\n", primes_used);
// Step 3: Find the TARGET-th squarefree index
int count = 0;
int target_index = -1;
for (int i = 1; i <= N; i++) {
if (!not_sqfree[i]) {
count++;
if (count == TARGET) {
target_index = i;
break;
}
}
}
if (target_index == -1) {
printf("ERROR: Sieve too small! Found only %d squarefree Fibonacci numbers.\n", count);
return 1;
}
printf("The %d-th squarefree Fibonacci number is F(%d)\n", TARGET, target_index);
// Step 4: Compute F(target_index) mod 10^16
ull MOD = 10000000000000000ULL; // 10^16
auto [fib_val, _] = fib_mod(target_index, MOD);
printf("Last 16 digits: %016llu\n", fib_val);
// Step 5: Compute scientific notation via log10
// F(n) ~ phi^n / sqrt(5) for large n
double log10_phi = log10((1.0 + sqrt(5.0)) / 2.0);
double log10_val = target_index * log10_phi - 0.5 * log10(5.0);
int exponent = (int)log10_val;
double mantissa = pow(10.0, log10_val - exponent);
// Round mantissa to 1 decimal place
mantissa = round(mantissa * 10.0) / 10.0;
if (mantissa >= 10.0) {
mantissa /= 10.0;
exponent++;
}
printf("Scientific notation: %.1fe%d\n", mantissa, exponent);
printf("\nAnswer: %016llu,%.1fe%d\n", fib_val, mantissa, exponent);
return 0;
}
/*
Compile and run:
g++ -O2 -std=c++17 -o solution solution.cpp
./solution
Expected output:
Answer: 1508395636674243,6.5e27330467
Complexity:
- Time: O(N log log N) for sieve, O(N) for rank computations
- Space: O(N) for boolean sieve
- N ~ 1.32 * 10^8
Key insight (Wall's conjecture):
p^2 | F(n) iff p * alpha(p) | n
where alpha(p) = smallest k with p | F(k)
*/
"""
Project Euler Problem 399: Squarefree Fibonacci Numbers
Find the 100,000,000th squarefree Fibonacci number.
Answer format: last 16 digits, comma, scientific notation (1 decimal).
Key idea: Under Wall's conjecture, F(n) is NOT squarefree iff
there exists a prime p such that p * alpha(p) | n,
where alpha(p) is the rank of apparition (smallest k with p | F(k)).
Algorithm:
1. Sieve to find primes
2. For each prime p, compute alpha(p) via Fibonacci mod p
3. Mark all multiples of p*alpha(p) in a boolean array
4. Count squarefree indices until we reach the 10^8-th one
5. Compute F(k) mod 10^16 and log10(F(k))
"""
import math
def sieve_primes(limit):
"""Sieve of Eratosthenes returning list of primes up to limit."""
is_prime = bytearray(b'\x01') * (limit + 1)
is_prime[0] = is_prime[1] = 0
for i in range(2, int(limit**0.5) + 1):
if is_prime[i]:
is_prime[i*i::i] = bytearray(len(is_prime[i*i::i]))
return [i for i in range(2, limit + 1) if is_prime[i]]
def rank_of_apparition(p):
"""Find smallest k > 0 such that F(k) = 0 (mod p)."""
a, b = 0, 1
for k in range(1, p * p + 10): # alpha(p) divides 2(p+1) at most
a, b = b, (a + b) % p
if a == 0:
return k
return -1 # Should not happen for valid primes
def fib_mod(n, mod):
"""Compute (F(n), F(n+1)) mod m using the doubling method."""
if n == 0:
return 0, 1
a, b = fib_mod(n >> 1, mod)
# F(2k) = F(k) * (2*F(k+1) - F(k))
# F(2k+1) = F(k)^2 + F(k+1)^2
c = a * ((2 * b - a) % mod) % mod
d = (a * a + b * b) % mod
if n & 1:
return d, (c + d) % mod
else:
return c, d
def log10_fib(n):
"""Compute log10(F(n)) using Binet's formula approximation.
For large n: F(n) ~ phi^n / sqrt(5)
log10(F(n)) ~ n * log10(phi) - 0.5 * log10(5)
"""
phi = (1 + math.sqrt(5)) / 2
return n * math.log10(phi) + math.log10(1 / math.sqrt(5))
def solve(target=100_000_000):
"""Find the target-th squarefree Fibonacci number."""
# Estimate sieve size: ~76.47% of Fibonacci numbers are squarefree
# Need about target / 0.7647 indices
N = int(target / 0.76) + 1000
# Add safety margin
N = max(N, target + target // 3)
print(f"Sieve size N = {N}")
print(f"Target: {target}-th squarefree Fibonacci number")
# Step 1: Generate primes
# We need primes p where p * alpha(p) <= N
# alpha(p) >= 1, so p <= N. But alpha(p) >= (p-1)/4 roughly,
# so p * (p-1)/4 <= N => p <= ~2*sqrt(N)
# To be safe, generate primes up to sqrt(2*N) + extra
prime_limit = int(math.sqrt(2 * N)) + 1000
# But small primes matter most. Also alpha(p) can be small for some p.
# Actually alpha(p) divides p-1 or 2(p+1), so alpha(p) <= 2(p+1)
# So p * alpha(p) <= p * 2(p+1) ~ 2p^2
# For p*alpha(p) <= N: p <= sqrt(N/2) ~ sqrt(N)
# But alpha(p) can be much smaller than p for large primes.
# However, for p > sqrt(N), even alpha(p)=1 gives p*1=p > sqrt(N),
# and we'd only mark ~N/p < sqrt(N) entries. Still need to consider.
# Let's be safe and go up to N (but that's too many primes).
# Actually: if p > N, then p*alpha(p) > N, no multiples in range.
# If p <= N, p*alpha(p) could be <= N only if alpha(p) is small enough.
# For most primes p, alpha(p) ~ p, so p*alpha(p) ~ p^2.
# Only need primes up to ~sqrt(N).
# But some primes have small alpha(p), e.g., alpha(p)=1 never happens (F(1)=1).
# alpha(p) >= 2 for p >= 2. So p*alpha(p) >= 2p, need p <= N/2.
# To be truly safe, use N/2 as limit, but compute alpha and skip if p*alpha > N.
prime_limit = N // 2 + 1
print(f"Generating primes up to {prime_limit}...")
primes = sieve_primes(prime_limit)
print(f"Found {len(primes)} primes")
# Step 2: Sieve non-squarefree indices
# not_squarefree[i] = True means F(i) is NOT squarefree
not_squarefree = bytearray(N + 1)
print("Computing ranks of apparition and sieving...")
primes_used = 0
for p in primes:
alpha = rank_of_apparition(p)
q = p * alpha # period for p^2 dividing F(n)
if q > N:
continue
primes_used += 1
# Mark all multiples of q
for j in range(q, N + 1, q):
not_squarefree[j] = 1
print(f"Primes contributing to sieve: {primes_used}")
# Step 3: Count squarefree indices to find the target-th one
count = 0
target_index = -1
for i in range(1, N + 1):
if not not_squarefree[i]:
count += 1
if count == target:
target_index = i
break
if target_index == -1:
print(f"ERROR: Sieve too small! Only found {count} squarefree Fibonacci numbers in range 1..{N}")
return None
print(f"The {target}-th squarefree Fibonacci number is F({target_index})")
# Step 4: Compute F(target_index) mod 10^16
MOD = 10**16
fib_val, _ = fib_mod(target_index, MOD)
last_16 = f"{fib_val:016d}"
print(f"Last 16 digits: {last_16}")
# Step 5: Compute scientific notation
log_val = log10_fib(target_index)
mantissa = 10 ** (log_val - int(log_val))
exponent = int(log_val)
sci_notation = f"{mantissa:.1f}e{exponent}"
print(f"Scientific notation: {sci_notation}")
answer = f"{last_16},{sci_notation}"
print(f"\nAnswer: {answer}")
return target_index, count, not_squarefree
def solve_small(target=200):
"""Solve for the 200th squarefree Fibonacci number (verification)."""
N = 500
primes = sieve_primes(N)
not_squarefree = bytearray(N + 1)
for p in primes:
alpha = rank_of_apparition(p)
q = p * alpha
if q > N:
continue
for j in range(q, N + 1, q):
not_squarefree[j] = 1
count = 0
for i in range(1, N + 1):
if not not_squarefree[i]:
count += 1
if count == target:
print(f"The {target}-th squarefree Fibonacci number is F({i})")
# Compute it exactly for small case
a, b = 0, 1
for _ in range(i):
a, b = b, a + b
fib_val = a
print(f"F({i}) = {fib_val}")
last_16 = str(fib_val)[-16:]
print(f"Last 16 digits: {last_16}")
log_val = log10_fib(i)
mantissa = 10 ** (log_val - int(log_val))
exponent = int(log_val)
print(f"Scientific notation: {mantissa:.1f}e{exponent}")
return i, fib_val
return None
def create_visualization(not_squarefree=None):
"""Create visualization of squarefree Fibonacci number properties."""