Browse Source

Final updates and revisions to the fibonacci post.

pull/7/head
jrtechs 6 years ago
parent
commit
a021a08fbc
1 changed files with 41 additions and 39 deletions
  1. +41
    -39
      blogContent/posts/programming/everything-fibonacci.md

+ 41
- 39
blogContent/posts/programming/everything-fibonacci.md View File

@ -3,11 +3,11 @@ know what the fibonacci sequence is and how to calculate it.
For those who don't know: [Fibonacci](https://en.wikipedia.org/wiki/Fibonacci) For those who don't know: [Fibonacci](https://en.wikipedia.org/wiki/Fibonacci)
is a sequence of numbers starting with 0,1 whose next number is the sum is a sequence of numbers starting with 0,1 whose next number is the sum
of the two previous numbers. After having multiple of my CS classes of the two previous numbers. After having multiple of my CS classes
gave lectures and multiple homework on the Fibonacci sequence; I decided
that it would be a great idea to write a blog post going over
give lectures and homeworks on the Fibonacci sequence; I decided
to write a blog post going over
the 4 main ways of calculating the nth term of the Fibonacci sequence. the 4 main ways of calculating the nth term of the Fibonacci sequence.
In addition to providing python code for calculating the nth perm of the sequence, a proof for their validity
and analysis of their time complexities both mathematically and empirically will
In addition to providing the python code for calculating the nth perm of the sequence, a proof for their validity
and an analysis of their time complexities both mathematically and empirically will
be examined. be examined.
# Slow Recursive Definition # Slow Recursive Definition
@ -25,28 +25,26 @@ def fib(n):
##Time Complexity ##Time Complexity
Observing that each call has two recursive calls we can place an upper bound on this Observing that each call has two recursive calls we can place an upper bound on this
function as $O(2^n)$. However, if we solve this recurrence we can compute the exact value
and place a tight bound for time complexity.
function as $O(2^n)$. However, if we solve this recurrence we can place a tight bound for time complexity.
We can write a recurrence for the number of times fib is called: We can write a recurrence for the number of times fib is called:
$$ $$
F(0) = 0\\
F(1) = 1\\ F(1) = 1\\
F(n) = F(n-1) + F(n-2)\\ F(n) = F(n-1) + F(n-2)\\
$$ $$
Next we replace each instance of F(n) with $a^n$ since we want to solve for the roots since that
will allow us to put a tight asymptotic limit on the growth.
Next, we replace F(n) with $a^n$ since we want to find rate of exponential growth.
$$ $$
a^n = a^{n-1} + a^{n-2}\\ a^n = a^{n-1} + a^{n-2}\\
\frac{a^n}{a^{n-2}} = \frac{a^{n-1} + a^{n-2}}{a^{n-2}}\\ \frac{a^n}{a^{n-2}} = \frac{a^{n-1} + a^{n-2}}{a^{n-2}}\\
a^2 = a + 1\\ a^2 = a + 1\\
a = \frac{1 + sqrt(5)}{2}\\
a = \frac{1 \pm sqrt(5)}{2}\\
$$ $$
From this calculation we can conclude that F(n) $\in \Theta 1.681^n$
From this calculation we can conclude that F(n) $\in \Theta 1.681^n$. We don't have to worry about
the negative root since it would not be asymptotically relevant by the definition of $\Theta$.
@ -57,14 +55,13 @@ Here is a graph of the actual performance that I observed from this recursive de
![Recursive Definition](media/fibonacci/RecursiveDefinition.png) ![Recursive Definition](media/fibonacci/RecursiveDefinition.png)
# Accumulation Solution # Accumulation Solution
The problem with the previous recursive solution is that you had to recalculate certain The problem with the previous recursive solution is that you had to recalculate certain
terms of fibonacci a ton of times. A summation variable would help us avoid this problem. terms of fibonacci a ton of times. A summation variable would help us avoid this problem.
You could write this using a simple loop, however, it is still possible to do this with
recursion.
You could write this solution using a simple loop or dynamic programming
, however, I chose to use recursion to demonstrate that it's recursion which made the first
problem slow.
```Python ```Python
@ -80,9 +77,11 @@ def fibIterative(n):
return fibHelper(n, 0, 1) return fibHelper(n, 0, 1)
``` ```
In this code example fibHelper is a method which accumulates the previous two terms.
In this code example, fibHelper is a method which accumulates the previous two terms.
The fibIterative is a wrapper method which sets the two initial terms equal to 0 and 1 The fibIterative is a wrapper method which sets the two initial terms equal to 0 and 1
representing the fibonacci sequence.
representing the fibonacci sequence. At first it may not be obvious that fibIterative(n)
is equivalent to fib(n). To demonstrate that these two are in fact equivalent, I broke this
into two inductive proofs.
## Proof for Fib Helper ## Proof for Fib Helper
**Lemma:** For any n $\epsilon$ N if n $>$ 1 then **Lemma:** For any n $\epsilon$ N if n $>$ 1 then
@ -90,7 +89,7 @@ representing the fibonacci sequence.
**Proof via Induction** **Proof via Induction**
Base Case: n = 2:
**Base Case**: n = 2:
$$ $$
LHS = fibHelper(2, a, b)\\ LHS = fibHelper(2, a, b)\\
= fibHelper(1, b, a + b) = a + b\\ = fibHelper(1, b, a + b) = a + b\\
@ -98,7 +97,7 @@ $$
= a + b\\ = a + b\\
$$ $$
Inductive Step:
**Inductive Step:**
Assume proposition is true for all n and show n+1 follows. Assume proposition is true for all n and show n+1 follows.
@ -118,19 +117,19 @@ $\Box$
**Proof via Strong Induction** **Proof via Strong Induction**
Base Case: n = 0:
**Base Case**: n = 0:
$$ $$
fibIterative(0, 0, 0) = 0\\ fibIterative(0, 0, 0) = 0\\
= fib(0) = fib(0)
$$ $$
Base Case: n = 1:
**Base Case**: n = 1:
$$ $$
fibIterative(1, 0, 0) = 1\\ fibIterative(1, 0, 0) = 1\\
= fib(1) = fib(1)
$$ $$
Inductive Step:
**Inductive Step:**
Assume proposition is true for all n and show n+1 follows. Assume proposition is true for all n and show n+1 follows.
@ -145,9 +144,9 @@ $\Box$
## Time Complexity ## Time Complexity
Suppose that we wish to solve for time complexity in terms of the number of additions needed to be
computed. By observing the algorithm for fibHelper we can see that we perform one addition every time
which we have a recursive call. We can now form a recurrence for time complexity and solve for it.
Suppose that we wish to solve for the time complexity in terms of the number of additions needed to be
computed. Based on fibHelper we can see that it performs one addition every recursive call.
We can now form a recurrence for time complexity.
$$ $$
T(0) = 0\\ T(0) = 0\\
@ -169,8 +168,8 @@ Fibonacci.
# Matrix Solution # Matrix Solution
We can actually get better than linear time for performance while calculating
the Fibonacci sequence recursively using this fact:
We can actually get better than linear for performance for Fibonacci while still using
recursion. However, to do so we need to know this fact:
$$ $$
\begin{bmatrix} \begin{bmatrix}
@ -183,10 +182,11 @@ F_n & F{n-1}
\end{bmatrix}^n \end{bmatrix}^n
$$ $$
Without any other tricks, raising a matrix to a power n times would not get
Without any tricks, raising a matrix to a power n times would not get
us better than linear performance. However, if we use the [Exponentiation by Squaring](https://en.wikipedia.org/wiki/Exponentiation_by_squaring) us better than linear performance. However, if we use the [Exponentiation by Squaring](https://en.wikipedia.org/wiki/Exponentiation_by_squaring)
method, we can expect to see logarithmic time. Since two spots in the matrix are always equal, method, we can expect to see logarithmic time. Since two spots in the matrix are always equal,
I represented the matrix as an array with only three elements.
I represented the matrix as an array with only three elements to reduce the space and
computations required.
```Python ```Python
@ -216,9 +216,9 @@ def fibPower(n):
## Time Complexity ## Time Complexity
For this algorythem lets solve for the time complexity as the number of additions and multiplications.
For this algorithm, lets solve for the time complexity as the number of additions and multiplications required.
Since we are always multiplying two 2x2 matrices, that is constant time.
Since we are always multiplying two 2x2 matrices, that operation is constant time.
$$ $$
T_{multiply} = 9 T_{multiply} = 9
@ -246,11 +246,14 @@ $$
T_{fibPower}(n) = T_{power}(n)\\ T_{fibPower}(n) = T_{power}(n)\\
$$ $$
Now we can state that $fibPower(n) \in \Theta(log(n))$.
## Inductive Proof for Matrix Method ## Inductive Proof for Matrix Method
I would like to now prove that this matrix identity is valid since it is not at first obvious.
**Lemma:** For any n $\epsilon$ N if n $>$ 0 then **Lemma:** For any n $\epsilon$ N if n $>$ 0 then
$$ $$
\begin{bmatrix} \begin{bmatrix}
@ -314,20 +317,20 @@ $\Box$
![FibPower Performance](media/fibonacci/FibPower.png) ![FibPower Performance](media/fibonacci/FibPower.png)
As expected by our mathmatical calcuations, the algorthem appears to be running in
As expected by our mathematical calculations, the algorithm appears to be running in
logarithmic time. logarithmic time.
## Measured Performance With Large Numbers ## Measured Performance With Large Numbers
![FibPower Performance](media/fibonacci/FibPowerBigPicture.png) ![FibPower Performance](media/fibonacci/FibPowerBigPicture.png)
When calculating the fibonacci term for extremely large numbers dispite having a polynomial
time complexity, the space required to compute Fibonacci grows exponentially. Since our
When calculating the fibonacci term for extremely large numbers despite having a polynomial
time complexity, the space required to compute each Fibonacci term grows exponentially. Since our
performance is only pseudo-polynomial we see a degrade in our performance when calculating performance is only pseudo-polynomial we see a degrade in our performance when calculating
large terms of the fibonacci sequence. large terms of the fibonacci sequence.
The one amazing thing to point out here is that despite calculating the 10,000 term of Fibonacci, The one amazing thing to point out here is that despite calculating the 10,000 term of Fibonacci,
this algorithm is nearly 400 times faster than the recursive algorithm when it was calculating
this algorithm is nearly 400 times faster than the recursive algorithm when calculating
the 30th term of Fibonacci. the 30th term of Fibonacci.
@ -346,10 +349,10 @@ def fibClosedFormula(n):
return (p-v)/math.sqrt(5) return (p-v)/math.sqrt(5)
``` ```
## Derivation of Formula
## Derivation of Binet's Formula
Similar to when we were calculating for the time complexity, we want to start by finding the
two roots of the equation.
Similar to when we were calculating the time complexity of the basic recursive definition
, we want to start by finding the two roots of the equation in terms of exponents.
$$ $$
a^n = a^{n-1} + a^{n-2}\\ a^n = a^{n-1} + a^{n-2}\\
@ -385,7 +388,6 @@ $$
## Time Complexity ## Time Complexity
Since we managed to find the closed form of the fibonacci sequence we can expect to see constant performance. Since we managed to find the closed form of the fibonacci sequence we can expect to see constant performance.
## Measured Performance ## Measured Performance

Loading…
Cancel
Save