2019-09-25 18:08:45 +00:00
|
|
|
"""Matrix Exponentiation"""
|
|
|
|
|
|
|
|
import timeit
|
|
|
|
|
|
|
|
"""
|
|
|
|
Matrix Exponentiation is a technique to solve linear recurrences in logarithmic time.
|
2020-05-22 06:10:11 +00:00
|
|
|
You read more about it here:
|
2022-10-16 07:43:29 +00:00
|
|
|
https://zobayer.blogspot.com/2010/11/matrix-exponentiation.html
|
2019-09-25 18:08:45 +00:00
|
|
|
https://www.hackerearth.com/practice/notes/matrix-exponentiation-1/
|
|
|
|
"""
|
|
|
|
|
|
|
|
|
2020-01-03 14:25:36 +00:00
|
|
|
class Matrix:
|
2019-09-25 18:08:45 +00:00
|
|
|
def __init__(self, arg):
|
2020-01-18 12:24:33 +00:00
|
|
|
if isinstance(arg, list): # Initializes a matrix identical to the one provided.
|
2019-09-25 18:08:45 +00:00
|
|
|
self.t = arg
|
|
|
|
self.n = len(arg)
|
2020-06-16 08:09:19 +00:00
|
|
|
else: # Initializes a square matrix of the given size and set values to zero.
|
2019-09-25 18:08:45 +00:00
|
|
|
self.n = arg
|
|
|
|
self.t = [[0 for _ in range(self.n)] for _ in range(self.n)]
|
|
|
|
|
|
|
|
def __mul__(self, b):
|
|
|
|
matrix = Matrix(self.n)
|
|
|
|
for i in range(self.n):
|
|
|
|
for j in range(self.n):
|
|
|
|
for k in range(self.n):
|
|
|
|
matrix.t[i][j] += self.t[i][k] * b.t[k][j]
|
|
|
|
return matrix
|
|
|
|
|
|
|
|
|
|
|
|
def modular_exponentiation(a, b):
|
|
|
|
matrix = Matrix([[1, 0], [0, 1]])
|
|
|
|
while b > 0:
|
|
|
|
if b & 1:
|
|
|
|
matrix *= a
|
|
|
|
a *= a
|
|
|
|
b >>= 1
|
|
|
|
return matrix
|
|
|
|
|
|
|
|
|
|
|
|
def fibonacci_with_matrix_exponentiation(n, f1, f2):
|
2024-11-20 11:28:43 +00:00
|
|
|
"""
|
|
|
|
Returns the n number of the Fibonacci sequence that
|
|
|
|
start with f1 and f2
|
|
|
|
Use the matrix exponentiation
|
|
|
|
>>> fibonacci_with_matrix_exponentiation(1, 5, 6)
|
|
|
|
5
|
|
|
|
>>> fibonacci_with_matrix_exponentiation(2, 10, 11)
|
|
|
|
11
|
|
|
|
>>> fibonacci_with_matrix_exponentiation(13, 0, 1)
|
|
|
|
144
|
|
|
|
>>> fibonacci_with_matrix_exponentiation(10, 5, 9)
|
|
|
|
411
|
|
|
|
>>> fibonacci_with_matrix_exponentiation(9, 2, 3)
|
|
|
|
89
|
|
|
|
"""
|
2019-09-25 18:08:45 +00:00
|
|
|
# Trivial Cases
|
|
|
|
if n == 1:
|
|
|
|
return f1
|
|
|
|
elif n == 2:
|
|
|
|
return f2
|
|
|
|
matrix = Matrix([[1, 1], [1, 0]])
|
|
|
|
matrix = modular_exponentiation(matrix, n - 2)
|
|
|
|
return f2 * matrix.t[0][0] + f1 * matrix.t[0][1]
|
|
|
|
|
|
|
|
|
|
|
|
def simple_fibonacci(n, f1, f2):
|
2024-11-20 11:28:43 +00:00
|
|
|
"""
|
|
|
|
Returns the n number of the Fibonacci sequence that
|
|
|
|
start with f1 and f2
|
|
|
|
Use the definition
|
|
|
|
>>> simple_fibonacci(1, 5, 6)
|
|
|
|
5
|
|
|
|
>>> simple_fibonacci(2, 10, 11)
|
|
|
|
11
|
|
|
|
>>> simple_fibonacci(13, 0, 1)
|
|
|
|
144
|
|
|
|
>>> simple_fibonacci(10, 5, 9)
|
|
|
|
411
|
|
|
|
>>> simple_fibonacci(9, 2, 3)
|
|
|
|
89
|
|
|
|
"""
|
2020-01-18 12:24:33 +00:00
|
|
|
# Trivial Cases
|
2019-09-25 18:08:45 +00:00
|
|
|
if n == 1:
|
|
|
|
return f1
|
|
|
|
elif n == 2:
|
|
|
|
return f2
|
|
|
|
|
|
|
|
fn_1 = f1
|
|
|
|
fn_2 = f2
|
|
|
|
n -= 2
|
|
|
|
|
|
|
|
while n > 0:
|
2024-11-20 11:28:43 +00:00
|
|
|
fn_2, fn_1 = fn_1 + fn_2, fn_2
|
2019-09-25 18:08:45 +00:00
|
|
|
n -= 1
|
|
|
|
|
2024-11-20 11:28:43 +00:00
|
|
|
return fn_2
|
2019-09-25 18:08:45 +00:00
|
|
|
|
|
|
|
|
|
|
|
def matrix_exponentiation_time():
|
|
|
|
setup = """
|
|
|
|
from random import randint
|
|
|
|
from __main__ import fibonacci_with_matrix_exponentiation
|
|
|
|
"""
|
|
|
|
code = "fibonacci_with_matrix_exponentiation(randint(1,70000), 1, 1)"
|
|
|
|
exec_time = timeit.timeit(setup=setup, stmt=code, number=100)
|
|
|
|
print("With matrix exponentiation the average execution time is ", exec_time / 100)
|
|
|
|
return exec_time
|
|
|
|
|
|
|
|
|
|
|
|
def simple_fibonacci_time():
|
|
|
|
setup = """
|
|
|
|
from random import randint
|
|
|
|
from __main__ import simple_fibonacci
|
|
|
|
"""
|
|
|
|
code = "simple_fibonacci(randint(1,70000), 1, 1)"
|
|
|
|
exec_time = timeit.timeit(setup=setup, stmt=code, number=100)
|
2019-10-05 05:14:13 +00:00
|
|
|
print(
|
|
|
|
"Without matrix exponentiation the average execution time is ", exec_time / 100
|
|
|
|
)
|
2019-09-25 18:08:45 +00:00
|
|
|
return exec_time
|
|
|
|
|
|
|
|
|
|
|
|
def main():
|
|
|
|
matrix_exponentiation_time()
|
|
|
|
simple_fibonacci_time()
|
|
|
|
|
|
|
|
|
|
|
|
if __name__ == "__main__":
|
|
|
|
main()
|