source of highlighter
plain | download
    1 #!/usr/bin/python3
    2 #
    3 # Written by Marek Behun
    4 # Oct 23, 2015
    5 # Public domain
    6 
    7 # φ = (1+√5)/2
    8 # ψ = (1-√5)/2
    9 # fib(n) = (φⁿ-ψⁿ) / √5
   10 
   11 def fib1(n):
   12         # Computation directly from definition
   13         # This is inaccurate for large n, because we are using floating point
   14         # arithmetic, which has limited precision
   15         from math import sqrt
   16         φ = (1 + sqrt(5)) / 2
   17         ψ = (1 - sqrt(5)) / 2
   18         return int((φ**n - ψ**n) / sqrt(5))
   19 
   20 # Let's try this by representing numbers of form a+b√5 as pairs (a, b)
   21 # and define multiplication, substraction and integer exponentiation
   22 # of such numbers
   23 
   24 def mul(x, y):
   25         # x = a+b√5
   26         # y = c+d√5
   27         # x·y = (a+b√5)·(c+d√5) = ac+ad√5+bc√5+bd√5√5 = (ac+5bd)+(ad+bc)√5
   28         (a, b) = x # x = a+b√5
   29         (c, d) = y # y = c+d√5
   30         return (a*c+5*b*d, a*d+b*c)
   31 
   32 def pow(x, n):
   33         # xⁿ = (a+b√5)ⁿ
   34         # We use exponentiation by squaring
   35         if n == 0:
   36                 # x⁰ = 1 = 1+0√5
   37                 return (1, 0)
   38         elif n == 1:
   39                 # x¹ = x
   40                 return x
   41         else:
   42                 if n % 2 == 0:
   43                         # for even n, xⁿ = (x²)ᵐ, m = n/2
   44                         return pow(mul(x, x), n // 2)
   45                 else:
   46                         # for odd n, xⁿ = x·xⁿ¯¹ = x·(x²)ᵐ, m = (n-1)/2
   47                         # for odd n, (n-1) // 2 = n // 2
   48                         return mul(x, pow(mul(x, x), n // 2))
   49 
   50 # But we can do exponentiation by squaring without recursion!
   51 def pow(x, n):
   52         if n == 0:
   53                 return (1, 0)
   54         y = (1, 0)
   55         while n > 1:
   56                 if n % 2 == 0:
   57                         x = mul(x, x)
   58                         n //= 2
   59                 else:
   60                         y = mul(x, y)
   61                         x = mul(x, x)
   62                         n //= 2
   63         return mul(x, y)
   64 
   65 def fib2(n):
   66         # Theorem: If (a+b√5)ⁿ = c+d√5, then (a-b√5)ⁿ = c-d√5.
   67         # Proof (by induction):
   68         #    For n = 0 and n = 1 the statement is true.
   69         #    For n ← n-1 we have:
   70         #        We know that (a+b√5)ⁿ¯¹ = c+d√5, then (a-b√5)ⁿ¯¹ = c-d√5.
   71         #        Now,
   72         #            (a+b√5)ⁿ = (c+d√5)·(a+b√5) = (ac+5bd)+(ad+bc)√5
   73         #        and
   74         #            (a-b√5)ⁿ = (c-d√5)·(a-b√5) = (ac+5bd)-(ad+bc)√5.
   75         #    Q.E.D.
   76 
   77         # fib(n) = (φⁿ - ψⁿ) / √5 = ((2φ)ⁿ - (2ψ)ⁿ) / (2ⁿ√5)
   78         # But using the theorem above for
   79         #    2φ = 1+√5
   80         #    2ψ = 1-√5
   81         # we know that if (2φ)ⁿ = a+b√5, then (2ψ)ⁿ = a-b√5, and so
   82         # fib(n) = ((2φ)ⁿ - (2ψ)ⁿ) / (2ⁿ√5) = 2b√5 / (2ⁿ√5) = b/2ⁿ¯¹
   83 
   84         # Therefore it is sufficient to compute only (2φ)ⁿ and take
   85         # the coefficient of √5:
   86         φ2 = (1, 1)
   87         (a, b) = pow(φ2, n)
   88 
   89         return 2*b // 2**n
   90 
   91 def fib3(n):
   92         # Inlining the exponentiation and removing code repetition we get
   93         if n == 0:
   94                 return 0
   95         x = (1, 1)
   96         y = (1, 0)
   97         _n = n
   98         while _n > 1:
   99                 if _n % 2 == 1:
  100                         y = mul(x, y)
  101                 x = mul(x, x)
  102                 _n //= 2
  103         a, b = mul(x, y)
  104         return b // 2**(n-1)
  105 
  106 def fib4(n):
  107         # We see that at the last step in fib3 we divide by 2ⁿ¯¹.
  108         # That means that there are n-1 redundant bits in the result
  109         # before we remove them. Those bits were added during the
  110         # execution of the while cycle, 2ⁱ bits in the x variable in
  111         # the i-th iteration starting from 1 and 1 bit in the y
  112         # variable everytime y is changed. Why?
  113         #
  114         # Both x₊ and y₊ at the end are some integer powers of x.
  115         # If x₀ and y₀ are x and y at the beginning and x₊ and y₊ at
  116         # the end, both x₊ and y₊ are some integer powers of x₀:
  117         #    x₊ = x₀ᵏ   and   y₊ = x₀ᵐ
  118         #
  119         #
  120         # It would save space in the variables if we get rid of these
  121         # bits during the cycle. By the theorems below, we can do this by
  122         # halving x at the end of each iteration and by halving y at the
  123         # end of each iteration in which y is changed but the first
  124         # (after the first iteration y has odd coefficients), and
  125         # then halve y after the cycle if it was changed at least once.
  126         #
  127         # Theorem: For every integer n ≥ 1 there are integers a and b
  128         #    with the same parity such that (1+√5)ⁿ = 2ⁿ¯¹·(a+b√5).
  129         # Proof (by induction):
  130         #    For n = 1 obviously holds (a = 1, b = 1, both odd).
  131         #    For n ← n-1 we have:
  132         #        (1+√5)ⁿ¯¹ = 2ⁿ¯²·(a+b√5)
  133         #        (1+√5)ⁿ = 2ⁿ¯²·(a+b√5)·(1+√5) = 2ⁿ¯²·((a+5b)+(a+b)√5)
  134         #        We know that a and b have the same parity.
  135         #        If they are both odd, there are some integers c and d
  136         #        such that a = 2c-1 and b = 2d-1. Then
  137         #            a+5b = 2c-1+5·(2d-1) = 2·(c+5d-3)
  138         #            a+b  = 2c-1+2d-1 = 2·(c+d-1)
  139         #        and
  140         #            (1+√5)ⁿ = 2ⁿ¯¹·((c+5d-3)+(c+d-1)√5).
  141         #        Here c+5d-3 and c+d-1 have the same parity, because
  142         #        their difference, 4d-2, is even.
  143         #        If they are both even, there are some integers c and d
  144         #        such that a = 2c and b = 2d. Then
  145         #            a+5b = 2c+10d = 2·(c+5d)
  146         #            a+b  = 2c+2d = 2·(c+d)
  147         #        and
  148         #            (1+√5)ⁿ = 2ⁿ¯¹·((c+5d)+(c+d)√5).
  149         #        Here c+5d and c+d have the same parity as well, because
  150         #        their difference, 4d, is even.
  151         #    Q.E.D.
  152         #
  153         # Theorem: If a and b are odd integers, then there are odd
  154         #    integers c and d such that (a+b√5)²/2 = c+d√5.
  155         # Proof:
  156         #    We have (a+b√5)² = (a²+5b²)+(2ab)√5, thus
  157         #        c = (a²+5b²)/2   and   d = ab.
  158         #    Oddity of c:
  159         #        Let a = 2e-1 and b = 2f-1, then
  160         #            c = (a²+5b²)/2 = ((4c²-4c+1)+5·(4d²-4d+1))/2 =
  161         #              = (4c²-4c+20d²-20d+6)/2 = 2c²-2c+20d²-20d+3 =
  162         #              = 2·(c²-c+10d²-10d)+3
  163         #        thus c is odd.
  164         #    Oddity of d:
  165         #        d is odd because it is a product of odd numbers.
  166         #    Q.E.D.
  167         #
  168         # Theorem: If A, B, C and D are odd integers, then there are
  169         #    integers E and F such that (A+B√5)·(C+D√5)/2 = E+F√5.
  170         # Proof:
  171         #    Let A = 2a-1, B = 2b-1, C = 2c-1 and D = 2d-1 for some
  172         #    integers a, b, c, d. Then we have
  173         #        (A+B√5)·(C+D√5) = ((2a-1) + (2b-1)√5) · ((2c-1) + (2d-1)√5) =
  174         #        = ((2a-1)·(2c-1) + 5·(2b-1)·(2d-1)) + ((2a-1)·(2d-1) + (2b-1)·(2c-1))√5 =
  175         #        = ((4ac-2a-2c+1) + 5·(4bd-2b-2d+1)) + ((4ad-2a-2d+1) + (4bc-2b-2c+1))√5 =
  176         #        =     (4ac-2a-2c+20bd-10b-10d+6)    +     (4ad-2a-2d+4bc-2b-2c+2)√5 =
  177         #        =      2·(2ac-a-c+10bd-5b-5d+3)     +      2·(2ad-a-d+2bc-b-c+1)√5
  178         #        = 2·(E+F)√5
  179         #    Q.E.D.
  180 
  181         if n == 0:
  182                 return 0
  183         x = (1, 1)
  184         y = (1, 0)
  185         j = 0
  186         while n > 1:
  187                 if n % 2 == 1:
  188                         a, b = mul(x, y)
  189                         if j == 1:
  190                                 a, b = a // 2, b // 2
  191                         j = 1
  192                         y = a, b
  193                 a, b = mul(x, x)
  194                 x = a // 2, b // 2
  195                 n //= 2
  196         a, b = mul(x, y)
  197         return b // 2**j
  198 
  199 def fib5(n):
  200         # After inlining multiplication
  201         if n == 0:
  202                 return 0
  203         a, b, c, d, j = 1, 1, 1, 0, 0
  204         while n > 1:
  205                 if n % 2 == 1:
  206                         # x·y = (a+b√5)·(c+d√5) = (ac+5bd)+(ad+bc)√5
  207                         c, d = a*c+5*b*d, a*d+b*c
  208                         if j == 1:
  209                                 c, d = c // 2, d // 2
  210                         j = 1
  211                 # x·x/2 = (a+b√5)·(a+b√5)/2 = (a²+5b²)/2 + ab√5
  212                 a, b = (a*a+5*b*b) // 2, a*b
  213                 n //= 2
  214         # x·y = (a+b√5)·(c+d√5) = (ac+5bd)+(ad+bc)√5
  215         return (a*d+b*c) // 2**j
  216 
  217 # Finally use bitwise operators instead of arithmetic
  218 def fib(n):
  219         "Returns the n-th Fibonacci number"
  220         if n == 0:
  221                 return 0
  222         a, b, c, d, j = 1, 1, 1, 0, 0
  223         while n > 1:
  224                 if n & 1 == 1:
  225                         c, d, j = a*c+5*b*d >> j, a*d+b*c >> j, 1
  226                 a, b, n = a*a+5*b*b >> 1, a*b, n >> 1
  227         return a*d+b*c >> j
  228 
  229 if __name__ == '__main__':
  230         for i in range(0,10):
  231                 print(fib(i))