Number Theory & Algebra contents
Fibonacci Numbers
Properties of Fibonacci numbers and four ways to compute F(n): linear, matrix power, fast doubling and modulo-p with the Pisano period.
Read first: Binary Exponentiation
The Fibonacci sequence is defined by
giving It appears in counting problems (tilings, compositions with parts 1 and 2), in the worst case of Euclid's algorithm, and in many data structures.
Properties
- Cassini's identity: .
- Addition rule: .
- Divisibility: , and more precisely .
- Sum: .
def fib_list(n):
f = [0, 1]
while len(f) <= n:
f.append(f[-1] + f[-2])
return f
F = fib_list(100)
from math import gcd
assert F[:11] == [0, 1, 1, 2, 3, 5, 8, 13, 21, 34, 55]
assert all(F[n - 1] * F[n + 1] - F[n] ** 2 == (-1) ** n for n in range(1, 90))
assert all(F[n + k] == F[k] * F[n + 1] + F[k - 1] * F[n] for n in range(1, 40) for k in range(1, 40))
assert all(gcd(F[m], F[n]) == F[gcd(m, n)] for m in range(1, 60) for n in range(1, 60))
assert all(sum(F[: n + 1]) == F[n + 2] - 1 for n in range(0, 90))Computing
Linearly,
Keep just the last two values. In Python the numbers are exact, but they grow (about bits), so for large each addition is not constant time.
def fib_linear(n):
a, b = 0, 1
for _ in range(n):
a, b = b, a + b
return a
assert fib_linear(10) == 55 and fib_linear(90) == 2880067194370816120Closed form (Binet)
Since , is the integer nearest to . With float it stays exact only up to ; for larger the precision is lost, and using exact arithmetic in is no simpler than the methods below.
from math import sqrt
def fib_binet(n):
phi = (1 + sqrt(5)) / 2
return round(phi ** n / sqrt(5))
assert all(fib_binet(n) == F[n] for n in range(70))
assert fib_binet(80) != F[80] # floats have run out of precisionMatrix exponentiation,
The transition is linear, so
and we raise the matrix to the -th power by binary exponentiation:
def fib_matrix(n, mod=None):
def mul(A, B):
C = [[A[0][0] * B[0][0] + A[0][1] * B[1][0], A[0][0] * B[0][1] + A[0][1] * B[1][1]],
[A[1][0] * B[0][0] + A[1][1] * B[1][0], A[1][0] * B[0][1] + A[1][1] * B[1][1]]]
return [[x % mod for x in row] for row in C] if mod else C
result, base = [[1, 0], [0, 1]], [[0, 1], [1, 1]]
while n:
if n & 1:
result = mul(result, base)
base = mul(base, base)
n >>= 1
return result[0][1]
assert [fib_matrix(i) for i in range(10)] == F[:10]
assert fib_matrix(100) == F[100]Fast doubling, with a smaller constant
From the addition rule with and one obtains:
So gives in three multiplications:
def fib_pair(n):
"""Return (F_n, F_{n+1})."""
if n == 0:
return 0, 1
a, b = fib_pair(n >> 1) # F_k, F_{k+1} with k = n // 2
c = a * (2 * b - a) # F_{2k}
d = a * a + b * b # F_{2k+1}
return (d, c + d) if n & 1 else (c, d)
def fib_fast(n):
return fib_pair(n)[0]
assert [fib_fast(i) for i in range(100)] == F[:100]
assert fib_fast(1000) == fib_linear(1000)
import sys
sys.set_int_max_str_digits(0) # see the note below
assert len(str(fib_fast(10 ** 5))) == 20899 # F_100000 has 20899 digitsThe recursion depth is , so it is safe. Because Python integers are exact, fib_fast(10**6) is instant even though the result has over 200,000 digits.
Modulo and the Pisano period
Working modulo , the sequence is periodic, because the pair can take only values and the recurrence is invertible. The period is called the Pisano period . It never exceeds , with equality for .
def pisano(m):
prev, cur = 0, 1
for i in range(1, 6 * m + 1):
prev, cur = cur, (prev + cur) % m
if prev == 0 and cur == 1:
return i
assert pisano(2) == 3 and pisano(3) == 8 and pisano(10) == 60
assert pisano(10 ** 3) == 1500
# huge index: reduce n modulo the period first
n, m = 10 ** 18, 1000
assert fib_matrix(n % pisano(m), m) == fib_matrix(n, m)For you don't need it, since fib_fast or fib_matrix(n, mod) is already logarithmic; the period is for problems asking about sums or all indices.
Fibonacci coding (Zeckendorf)
Every positive integer is a unique sum of non-consecutive Fibonacci numbers. Greedy works: repeatedly subtract the largest Fibonacci number that fits.
def zeckendorf(n):
fibs = [1, 2]
while fibs[-1] <= n:
fibs.append(fibs[-1] + fibs[-2])
parts = []
for f in reversed(fibs):
if f <= n:
parts.append(f)
n -= f
return parts
assert zeckendorf(100) == [89, 8, 3]
assert zeckendorf(4) == [3, 1]
assert all(sum(zeckendorf(n)) == n for n in range(1, 500))Practice problems
- SPOJ - Euclid Algorithm Revisited
- SPOJ - Fibonacci Sum
- HackerRank - Is Fibo
- Project Euler - Even Fibonacci numbers
- DMOJ - Fibonacci Sequence
- DMOJ - Fibonacci Sequence (Harder)
- DMOJ UCLV - Numbered sequence of pencils
- DMOJ UCLV - Fibonacci 2D
- DMOJ UCLV - fibonacci calculation
- LightOJ - Number Sequence
- Codeforces - C. Fibonacci
- Codeforces - A. Hexadecimal's theorem
- Codeforces - B. Blackboard Fibonacci
- Codeforces - E. Fibonacci Number