diff --git a/algorithms/C/README.md b/algorithms/C/README.md index 0ca7c4c9..9e241b51 100644 --- a/algorithms/C/README.md +++ b/algorithms/C/README.md @@ -36,6 +36,7 @@ - [Palindrome Number](maths/palindrome.c) - [Fibonacci Series](maths/fibonacci-series.c) - [Odd or Even Number](maths/odd-or-even-number.c) +- [Fibonacci Number](maths/fibonacci-number/README.md) ## Queues diff --git a/algorithms/C/maths/fibonacci-number/.gitignore b/algorithms/C/maths/fibonacci-number/.gitignore new file mode 100644 index 00000000..2441f03a --- /dev/null +++ b/algorithms/C/maths/fibonacci-number/.gitignore @@ -0,0 +1,4 @@ +main +# binary files +*.o +*.exe \ No newline at end of file diff --git a/algorithms/C/maths/fibonacci-number/Makefile b/algorithms/C/maths/fibonacci-number/Makefile new file mode 100644 index 00000000..8214801c --- /dev/null +++ b/algorithms/C/maths/fibonacci-number/Makefile @@ -0,0 +1,4 @@ +run: main.c + .\a.exe +main.c: + gcc .\src\main.c .\src\fib.c -lm diff --git a/algorithms/C/maths/fibonacci-number/README.md b/algorithms/C/maths/fibonacci-number/README.md new file mode 100644 index 00000000..7d5101c1 --- /dev/null +++ b/algorithms/C/maths/fibonacci-number/README.md @@ -0,0 +1,59 @@ +# Fibonacci Number +Fibonacci numbers form a Fibonacci sequence where given any number (excluding first 2 terms) is a sum of its two preceding numbers. Usually, the sequence is either start with 0 and 1 or 1 and 1. Below is a Fibonacci sequnce starting from 0 and 1: + +$$ +0, 1, 1, 2, 3, 5, 8, 13, 21, 34, 55, 89, 144, \dots +$$ + +The problem is to calculate the n-th term Fibonacci number given two starting numbers. + +## Prerequisites +- C compiler (or IDE) +- MAKE software (optional if you compile the source files manually) + + +## Instructions +- using makefile + ```bash + make # or mingw32-make + ``` +- compile using gcc + ``` + cd \fibonacci-number + gcc .\src\main.c + ``` +## Note +The sequence can be described by a recurrent function as below: + +$$ +\begin{align*} + F(0) &= 0 \\ + F(1) &= 1 \\ + F(n) &= F(n-1) + F(n-2) +\end{align*} +$$ + +- This provides a direct recursive implementation. The time complexity is $O(2^n)$. It can be improved through memomization. +- It can done iteratively using 2 more states variables. The time complexity is $O(n)$. +- There exists a clever logarithmic algorithm $O(\log{n})$ in computing n-th term Fibonacci number. The computations can be in form of matrix multiplication, then we can devise some form of Ancient Egyptian multiplication (i.e.: double and squaring) to improve the algorithm. [reference](https://rybczak.net/2015/11/01/calculation-of-fibonacci-numbers-in-logarithmic-number-of-steps/) +- Lastly, there also exist a formula to approximate n-term Fibonacci number $O(1)$. [reference](https://mathworld.wolfram.com/BinetsFibonacciNumberFormula.html) + +The given implementations shall assume that the Fibonacci sequence is starting from 0 and 1. The reader may try to generalize it to certain extent as a practice. + +## Test Cases & Output + +1. Example output of calling function: +``` +/* some code */ +printf("%d", iter_fib(7)); +printf("%d\n", memo_fib(7)); +/* some code */ +``` + +``` +13 +13 +``` +2. The code should yield the same output as other version. + +3. The sum of even Fibonacci numbers below 4000000 should be 4613732. [Adapted from Project Euler.net](https://projecteuler.net/problem=2) diff --git a/algorithms/C/maths/fibonacci-number/src/fib.c b/algorithms/C/maths/fibonacci-number/src/fib.c new file mode 100644 index 00000000..41196239 --- /dev/null +++ b/algorithms/C/maths/fibonacci-number/src/fib.c @@ -0,0 +1,92 @@ +#include"fib.h" +#include // sqrt, pow are used in binet_fib + +int recur_fib(int n){ + if(n == 0 || n == 1) + return n; + else + return recur_fib(n-1) + recur_fib(n-2); +} +int iter_fib(int n){ + if(n == 0 || n == 1) + return n; + int prev2 = 0; + int prev1 = 1; + int res = prev1 + prev2; + for(n = n - 2; n > 0; --n){ + prev2 = prev1; + prev1 = res; + res = prev1 + prev2; + } + return res; +} + +// it should be called before using the function memo_fib() +void memomizing_fib(){ + memomized_fib[0] = 0; + memomized_fib[1] = 1; + for(int i = 2; i < MAXSIZE; ++i) + memomized_fib[i] = memomized_fib[i-1]+memomized_fib[i-2]; +} + +// return memomized value if exists, or else compute it as usual +int memo_fib(int n){ + if(n < MAXSIZE && n >= 0) + return memomized_fib[n]; + else + return memo_fib(n-1) + memo_fib(n-2); +} +/** + * fibonacci based linear transformation (linear algebra) + * reference: https://rybczak.net/2015/11/01/calculation-of-fibonacci-numbers-in-logarithmic-number-of-steps/ +*/ +int iter_log_fib(int n){ + int a = 0; + int b = 1; + int p = 0; + int q = 1; + while(n > 0){ + if(n%2 == 0){ + int prev_p = p; + int prev_q = q; + p = prev_p*prev_p + prev_q*prev_q; + q = (2*prev_p + prev_q)*prev_q; + n = n/2; + } + else{ + int prev_a = a; + int prev_b = b; + --n; + a = p*prev_a + q*prev_b; + b = q*prev_a + (p+q)*prev_b; + } + } + return a; +} + +/** + * fibonacci based linear transformation (linear algebra) + * reference: https://rybczak.net/2015/11/01/calculation-of-fibonacci-numbers-in-logarithmic-number-of-steps/ +*/ +int log_fib_helper(int n, int a, int b, int p, int q){ + if(n == 0) + return a; + else if(n%2 == 0) + return log_fib_helper(n/2, a, b, p*p+q*q, (2*p+q)*q); + else + return log_fib_helper(n-1, p*a + q*b, q*a+(p+q)*b, p, q); +} + +int log_fib(int n){ + return log_fib_helper(n,0,1,0,1); +} +/** + * Closed form formula + * reference: https://mathworld.wolfram.com/BinetsFibonacciNumberFormula.html +*/ +int binet_fib(int n){ + const double golden_ratio = (1+sqrt(5))/2; + const double conjugate_golden_ratio = 1-golden_ratio; + double res = (pow(golden_ratio,n) - pow(conjugate_golden_ratio, n))/sqrt(5); + return round(res); +} diff --git a/algorithms/C/maths/fibonacci-number/src/fib.h b/algorithms/C/maths/fibonacci-number/src/fib.h new file mode 100644 index 00000000..c770d96d --- /dev/null +++ b/algorithms/C/maths/fibonacci-number/src/fib.h @@ -0,0 +1,22 @@ +#ifndef FIB_H_INCLUDED +#define FIB_H_INCLUDED + +/** + * fib(n) takes nonnegative number n + * return n-th term fibonacci number. + * The prefix highlights its algorithm used + */ + +int recur_fib(int n); +int iter_fib(int n); + +#define MAXSIZE 30 +int memomized_fib[MAXSIZE]; +void memomizing_fib(); // it should be called before using the function memo_fib() + +int memo_fib(int n); +int iter_log_fib(int n); +int log_fib(int n); +int binet_fib(int n); + +#endif \ No newline at end of file diff --git a/algorithms/C/maths/fibonacci-number/src/main.c b/algorithms/C/maths/fibonacci-number/src/main.c new file mode 100644 index 00000000..8bc173fc --- /dev/null +++ b/algorithms/C/maths/fibonacci-number/src/main.c @@ -0,0 +1,23 @@ +#include +#include"fib.h" + +int main(){ + memomizing_fib(); // this is to initialize the memomized table + int n = 15; + printf("%d\n", recur_fib(n)); // it becomes slow as n get larger for recur_fib + for(int i = 0; i <= 35; ++i){ + printf("n = %d\t", i); + printf(" %d", iter_fib(i)); + printf(" %d", memo_fib(i)); + printf(" %d", log_fib(i)); + printf(" %d", binet_fib(i)); + printf(" %d\n", iter_log_fib(i)); + } + int sum = 0; + int bound = 4000000; + for(int i = 0; memo_fib(i) < bound; ++i) + if(memo_fib(i)%2 == 0) + sum += memo_fib(i); + printf("The sum of even Fibonacci number below %d = %d", bound, sum); + return 0; +}