Advertisement
cat_baxter

A fast algorithm for computing large Fibonacci numbers

Jul 27th, 2012
73
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
Python 3.51 KB | None | 0 0
  1. # -*- coding: utf-8 -*-
  2.  
  3. import time, random
  4.  
  5. class MatrixFibonacci:
  6.     A = [[1, 1],
  7.          [1, 0]]
  8.  
  9.     def __init__(self):
  10.         self.__memo = {}
  11.  
  12.  
  13.     # умножение матриц
  14.     # ожидаются матрицы в виде списка список размером 2x2
  15.     def __multiply_matrices(self, M1, M2):
  16.         a11 = M1[0][0]*M2[0][0] + M1[0][1]*M2[1][0]
  17.         a12 = M1[0][0]*M2[0][1] + M1[0][1]*M2[1][1]
  18.         a21 = M1[1][0]*M2[0][0] + M1[1][1]*M2[1][0]
  19.         a22 = M1[1][0]*M2[0][1] + M1[1][1]*M2[1][1]
  20.         r = [[a11, a12], [a21, a22]]
  21.         return r
  22.  
  23.  
  24.     # возведение матрицы в степень
  25.     # ожидается p равная степени двойки
  26.     def __get_matrix_power(self, M, p):
  27.         if p == 1:
  28.             return M
  29.         if p in self.__memo:
  30.             return self.__memo[p]
  31.         K = self.__get_matrix_power(M, int(p/2))
  32.         R = self.__multiply_matrices(K, K)
  33.         self.__memo[p] = R
  34.         return R
  35.  
  36.  
  37.     # получение n-го числа Фибоначчи
  38.     # в качестве n ожидается неотрицательное целое число
  39.     def get_number(self, n):
  40.         if n == 0:
  41.             return 0
  42.         if n == 1:
  43.             return 1
  44.         # разложение переданной степени на степени, равные степени двойки
  45.         # т.е. 62 = 2^5 + 2^4 + 2^3 + 2^2 + 2^0 = 32 + 16 + 8 + 4 + 1
  46.         powers = [ int(pow(2, b)) for (b, d) in enumerate(reversed(bin(n-1)[2:])) if d == '1' ]
  47.         # тоже самое, но менее pythonic: http://pastebin.com/h8cKDkHX
  48.  
  49.         matrices = [ self.__get_matrix_power(MatrixFibonacci.A, p) for p in powers ]
  50.         while len(matrices) > 1:
  51.             M1 = matrices.pop()
  52.             M2 = matrices.pop()
  53.             R = self.__multiply_matrices(M1, M2)
  54.             matrices.append(R)
  55.         return matrices[0][0][0]
  56.  
  57.  
  58. class IterationFibonacci:
  59.     def __init__(self):
  60.         pass
  61.  
  62.     # получение n-го числа Фибоначчи
  63.     # в качестве n ожидается неотрицательное целое число
  64.     def get_number(self, n):
  65.         if n == 0:
  66.             return 0
  67.         a = 0
  68.         b = 1
  69.         c = 1
  70.         for i in range(n-1):
  71.             c = a + b
  72.             a = b
  73.             b = c
  74.         return c
  75.  
  76.  
  77. import time, sys
  78. from math import log, floor
  79.  
  80. # A fast algorithm for computing large Fibonacci numbers
  81. # http://www.ii.uni.wroc.pl/~lorys/IPL/article75-6-1.pdf
  82. # used for http://weblogs.java.net/blog/kabutz/archive/2012/02/24/fibonacci-1000000000-challenge
  83.  
  84. def fib(n):
  85.     if n == 0:
  86.         return n
  87.     F, L, sign, exp = 1, 1, -2, int(floor(log (n,2)))
  88.     mask = 2**exp
  89.     for i in xrange (exp - 1):
  90.         mask = mask >> 1
  91.         F2   = F**2
  92.         FL2  = (F + L)**2
  93.         F    = ((FL2 - 6*F2) >> 1) - sign
  94.         if n & mask:
  95.             temp = (FL2 >> 2 ) + F2
  96.             L     = temp + (F << 1)
  97.             F     = temp
  98.         else:
  99.             L    = 5*F2 + sign
  100.         sign = -2 if n & mask else 2
  101.     if n & (mask >> 1) == 0:
  102.         return F * L
  103.     else:
  104.         return ((F + L) >> 1) * L - (sign >> 1)
  105.  
  106.  
  107. def benchmark():
  108.  
  109.     N = 1000000
  110.  
  111.     t = time.time()
  112.     mfib = MatrixFibonacci()
  113.     m = mfib.get_number(N)
  114.     print "Time (s): %f" % (time.time()-t)
  115.  
  116.     s = time.time()
  117.     fib(N)
  118.     print("Time (s): %f" % (time.time() - s))
  119.  
  120. benchmark()
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement