Zeckendorf Representation
Every positive integer has a unique Zeckendorf representation as a sum of non-consecutive Fibonacci numbers. Let z(n) denote the number of terms in this representation. For example, z(5) = 1, z(14)...
Problem Statement
This archive keeps the full statement, math, and original media on the page.
Each new term in the Fibonacci sequence is generated by adding the previous two terms.
Starting with \(1\) and \(2\), the first \(10\) terms will be: \(1, 2, 3, 5, 8, 13, 21, 34, 55, 89\).
Every positive integer can be uniquely written as a sum of nonconsecutive terms of the Fibonacci sequence. For example, \(100 = 3 + 8 + 89\).
Such a sum is called the
For any integer \(n > 0\), let \(z(n)\) be the number of terms in the Zeckendorf representation of \(n\).
Thus, \(z(5) = 1\), \(z(14) = 2\), \(z(100) = 3\) etc.
Also, for \(0 < n < 10^6\), \(\sum z(n) = 7894453\).
Find \(\sum z(n)\) for \(0 < n < 10^{17}\).
Problem 297: Zeckendorf Representation
Mathematical Foundation
We use the Fibonacci sequence defined by with .
Theorem 1 (Zeckendorf’s Theorem). Every positive integer has a unique representation where and contains no two consecutive indices.
Proof. (Existence) By strong induction. For , the result holds. For , let be the largest Fibonacci number . Then (since ). By induction, has a Zeckendorf representation using Fibonacci numbers (no appears because ), so the representation is non-consecutive.
(Uniqueness) Suppose with . Let be the largest index in . WLOG . Then (since is the largest in the symmetric difference and elements match). But (by a standard Fibonacci identity), a contradiction since the -sum must account for .
Theorem 2 (Recurrence for at Fibonacci Boundaries). Define . Then
Proof. The integers in split into and . The first group contributes .
For the second group: each integer can be written uniquely as where . Since and , we have . The Zeckendorf representation of is plus the Zeckendorf representation of (which uses only since , ensuring no consecutive indices with ).
Thus for , and (when ). The total contribution is:
Hence .
Theorem 3 (General Recursion). For :
Proof. Split into and . The first contributes . For the second, each has as above. There are such integers, each contributing the extra for the term, plus . The residuals range over , contributing .
Lemma 1 (Recursion Depth). The recursion in Theorem 3 terminates in steps, where .
Proof. At each step, is replaced by where (since , and actually follows from and ). Thus , and after steps, is reduced to at most . The recursion terminates when , requiring steps.
Editorial
Fibonacci: F[0]=1, F[1]=2, F[2]=3, F[3]=5, … S(N) = sum of z(n) for n in [1, N). At Fibonacci boundaries: S(F[k+1]) = S(F[k]) + S(F[k-1]) + F[k-1] General recursion (F[k] <= N < F[k+1]): S(N) = S(F[k]) + (N - F[k]) + S(N - F[k]). We precompute Fibonacci numbers and S(F_k). Finally, recursive computation of S(N).
Pseudocode
Precompute Fibonacci numbers and S(F_k)
Recursive computation of S(N)
Complexity Analysis
- Time: for precomputing Fibonacci numbers and their -values, plus recursive steps.
- Space: for storing Fibonacci numbers and values (about 83 entries for ).
Answer
Code
Each problem page includes the exact C++ and Python source files from the local archive.
#include <bits/stdc++.h>
using namespace std;
// Problem 297: Zeckendorf Representation
//
// Find sum of z(n) for 0 < n < 10^17, where z(n) = number of terms
// in the Zeckendorf representation of n.
//
// Fibonacci: F[1]=1, F[2]=2, F[3]=3, F[4]=5, ...
//
// S(N) = sum of z(n) for n in [1, N).
//
// At Fibonacci boundaries:
// S(F[k+1]) = S(F[k]) + S(F[k-1]) + F[k-1]
//
// General recursion (for F[k] <= N < F[k+1]):
// S(N) = S(F[k]) + (N - F[k]) + S(N - F[k])
//
// Each recursive call reduces N, and the sequence of remainders follows
// a Zeckendorf-like decomposition, terminating in O(log N) steps.
int main(){
long long N = 100000000000000000LL; // 10^17
// Generate Fibonacci numbers: F[1]=1, F[2]=2, F[3]=3, ...
vector<long long> F;
F.push_back(1);
F.push_back(2);
while (F.back() < N) {
F.push_back(F[F.size()-1] + F[F.size()-2]);
}
int M = F.size(); // F[0]..F[M-1] = F_1..F_M
// Precompute S(F[k]) for k = 1..M
// S(F[1]) = S(1) = 0 (sum over empty range [1,1))
// S(F[2]) = S(2) = z(1) = 1
// S(F[k+1]) = S(F[k]) + S(F[k-1]) + F[k-1]
// Using 0-indexed: S_fib[i] = S(F[i])
vector<long long> SF(M + 1, 0);
// SF[0] = S(F[0]) = S(1) = 0
// SF[1] = S(F[1]) = S(2) = z(1) = 1
SF[0] = 0;
if (M >= 2) SF[1] = 1;
for (int i = 2; i < M; i++) {
// S(F[i]) = S(F[i-1]) + S(F[i-2]) + F[i-2]
// In 0-indexed: F[i] corresponds to F_{i+1}, F[i-2] = F_{i-1}
// Wait, let me re-index carefully.
// F[0] = F_1 = 1, F[1] = F_2 = 2, F[2] = F_3 = 3, ...
// S(F_{k+1}) = S(F_k) + S(F_{k-1}) + F_{k-1}
// In 0-indexed: S(F[i]) = S(F[i-1]) + S(F[i-2]) + F[i-2]
SF[i] = SF[i-1] + SF[i-2] + F[i-2];
}
// Now compute S(N) recursively
// S(N): find largest F[k] <= N, then S(N) = SF[k] + (N - F[k]) + S(N - F[k])
// where k is 0-indexed into F array.
// Note: N - F[k] < F[k-1], so recursion terminates.
function<long long(long long)> S = [&](long long n) -> long long {
if (n <= 1) return 0; // S(1) = 0, S(0) = 0
// Find largest F[k] <= n
// F is sorted, use upper_bound
int k = (int)(upper_bound(F.begin(), F.end(), n) - F.begin()) - 1;
// F[k] <= n < F[k+1]
if (F[k] == n) return SF[k];
return SF[k] + (n - F[k]) + S(n - F[k]);
};
cout << S(N) << endl;
return 0;
}
"""
Project Euler Problem 297: Zeckendorf Representation
Find sum of z(n) for 0 < n < 10^17, where z(n) = number of terms
in the Zeckendorf representation of n.
Fibonacci: F[0]=1, F[1]=2, F[2]=3, F[3]=5, ...
S(N) = sum of z(n) for n in [1, N).
At Fibonacci boundaries:
S(F[k+1]) = S(F[k]) + S(F[k-1]) + F[k-1]
General recursion (F[k] <= N < F[k+1]):
S(N) = S(F[k]) + (N - F[k]) + S(N - F[k])
"""
import bisect
def solve():
N = 10**17
# Generate Fibonacci: F[0]=1, F[1]=2, F[2]=3, F[3]=5, ...
F = [1, 2]
while F[-1] < N:
F.append(F[-1] + F[-2])
M = len(F)
# Precompute S(F[k]) for k = 0..M-1
SF = [0] * M
SF[0] = 0 # S(1) = 0
if M >= 2:
SF[1] = 1 # S(2) = z(1) = 1
for i in range(2, M):
SF[i] = SF[i-1] + SF[i-2] + F[i-2]
def S(n):
if n <= 1:
return 0
k = bisect.bisect_right(F, n) - 1
if F[k] == n:
return SF[k]
return SF[k] + (n - F[k]) + S(n - F[k])
print(S(N))
if __name__ == "__main__":
solve()