Skip to content

Commit c3d6a8e

Browse files
authored
Add readme
1 parent bdc5682 commit c3d6a8e

File tree

1 file changed

+259
-1
lines changed

1 file changed

+259
-1
lines changed

README.md

Lines changed: 259 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1,259 @@
1-
# dynamic-programming
1+
### What is Dynamic programming?
2+
3+
Dynamic programming is a computer programming method used to avoid computing
4+
multiple time the same subproblem in a recursive algorithm.
5+
6+
Dynamic programming is applied to optimization problems where can be many possible solutions.
7+
Each solution has a value what can be find by solution with the optimal (minimum or maximum) value.
8+
This solution call an optimal solution to the problem.
9+
10+
Dynamic programming method can design in 4 steps:
11+
1. Characterize the structure of an optimal solution.
12+
2. Recursively define the value of an optimal solution.
13+
3. Compute the value of an optimal solution in a bottom-up fashion.
14+
4. Construct an optimal solution from computed information.
15+
16+
<b>*</b>Some of the alogirthm problem can be solved by the "divide and conquer" strategy instead of Dynamic programming.
17+
For example:
18+
- Merge sort
19+
- Quick sort
20+
21+
Solutions for this algorithms not overlapping sub-problems, so they not classified as dynamic programming problems.
22+
23+
### The Fibonacci
24+
25+
Numbers( 0,1,1,2,3,5,8,13,21,34...), it’s the Fibonacci sequence, described by the recursive formula:
26+
27+
```
28+
29+
F(0) = 0, (1) = 1
30+
F(N) = F(N - 1) + F(N - 2), for N > 1.
31+
32+
```
33+
34+
Fibonacci sequence which approximates the [golden spiral](https://en.wikipedia.org/wiki/Golden_ratio#Relationship_to_Fibonacci_sequence).
35+
36+
The spiral is drawn starting from the inner 1×1 square and continues outwards to successively
37+
larger squares.
38+
39+
![Vizualization Fibonacci sequence](https://en.wikipedia.org/wiki/Golden_ratio#/media/File:FakeRealLogSpiral.svg)
40+
41+
Commonly denoted F(n) form a sequence, such that each number is the
42+
sum of the two preceding ones, starting from 0 and 1.
43+
44+
Given N, calculate F(N).
45+
Fibonacci sequence most common example to Dynamic programming method.
46+
47+
Trivial algorithm for computing F(N):
48+
```
49+
50+
if n = 0: return 0
51+
else if n = 1: return 1
52+
else: return naive F(n − 1) + F(n − 2)
53+
```
54+
55+
JavaScript implementation:
56+
57+
```js
58+
59+
/**
60+
* Time complexity: O(2^n)
61+
* Space complexity: O(n)
62+
*
63+
* @param number N
64+
* @return number
65+
*/
66+
function getFibonacciNumberRecursive(N) {
67+
if (N <= 1) {
68+
return N;
69+
}
70+
return getFibonacciNumberRecursive(N - 1) + getFibonacciNumberRecursive(N - 2);
71+
}
72+
```
73+
74+
### Runtime Analysis:
75+
76+
To calculate F(n) as sum of time to calculate F(n-1) and F(n-2), plus time to calculate them together O(1):
77+
```
78+
79+
T(n<=1) = O(1) - for all operations F(1), F(0)
80+
81+
T(n) = T(n − 1) + T(n − 2) + O(1)
82+
T(n) = T(n − 1) + T(n − 2) + c
83+
2^kT(n − 2 * k) + c(2^(k−1) + 2^(k−2) + . . . + 2 + 1) = Ω(c2 ^ (n/2))
84+
85+
```
86+
87+
Where "c" is the time needed to add n-bit numbers. Since
88+
89+
```
90+
T(n) = Ω(n2 ^ n/2) => T(n) = O(2^n)
91+
92+
```
93+
94+
<b>O(2^n)</b> - exponential time and O(n) space complexity for call stack size.
95+
96+
To describe <b>O(2^n)</b> time complexity, lets draw the recursion tree of calls,
97+
which will have depth n and intuitively figure out that this function
98+
is asymptotically <b>O(2^n)</b>.
99+
100+
To prove this conjecture by induction, let shows recursion tree for F(5)
101+
102+
```
103+
104+
/<------------- F(5) ------------------>\
105+
/ \
106+
F(4)[F(n - 1)] F(3)[F(n - 2)]
107+
/ \ / \
108+
/ \ / \
109+
F(3)[F(n - 1)] F(2)[F(n - 2)] F(2)[F(n - 1)] F(1)[F(n - 2)]
110+
/ \ / \ / \
111+
/ \ / \ / \
112+
F(2)[F(n - 1)] F(2)[F(n - 1)] F(1)[F(n - 1)] F(0)[F(n - 2)] F(1)[F(n - 1)] F(0)[F(n - 2)]
113+
/ \
114+
/ \
115+
F(1)[F(n - 1)] F(0)[F(n - 2)]
116+
117+
```
118+
119+
In provide recursion tree of calls example, getFibonacciNumberRecursive(5) or F(5) function make
120+
multiple execution with same arguments:
121+
- F(2) - 4 times
122+
- F(3) - 2 times
123+
- The leaves of the recursion tree will always return 1 (F(1) and F(0))
124+
125+
Assume T(n-1) = O(2^(n-1)), therefore
126+
127+
```
128+
129+
T(n) = T(n-1) + T(n-2) + O(1) which is equal to
130+
T(n) = O(2 ^ (n-1)) + O(2 ^ (n-2)) + O(1) = O(2^n)
131+
132+
```
133+
134+
Consequently, the tight bound for this function is the Fibonacci sequence itself (~θ(1.6^n))
135+
whitch related to Golden ratio
136+
137+
![Vizualization Golden ratio with numbers](https://en.wikipedia.org/wiki/Fibonacci_number#/media/File:FibonacciSpiral.svg)
138+
139+
### Improved Fibonacci Algorithm by memoization.
140+
141+
Simple way to optimeze function getFibonacciNumberRecursive, create a chache Object for memoize storage.
142+
Runtime, assuming n-bit registers for each entry of memo data structure(cache Object):
143+
144+
```
145+
146+
T(n) = T(n − 1) + O(1) = O(n)
147+
T(n) = T(n − 1) + c = O(cn)
148+
T(n) = O(n ^ 2)
149+
```
150+
151+
JavaScript implementation:
152+
153+
```js
154+
/*
155+
* @param memo - cache storage object
156+
*/
157+
const memo = {};
158+
159+
/**
160+
* Time complexity: O(n ^ 2)
161+
* Space complexity: O(n)
162+
*
163+
* @param number N
164+
* @return number
165+
*/
166+
function getFibonacciNumberRecursive(N) {
167+
if (N <= 1) {
168+
return N;
169+
}
170+
if (!memo[N]) {
171+
memo[N] = getFibonacciNumberRecursive(N - 1) + getFibonacciNumberRecursive(N - 2);
172+
}
173+
return memo[N];
174+
}
175+
```
176+
177+
In recursion tree of calls example, (5) or F(5) function make
178+
one execution with same arguments:
179+
- F(2) - 1 times
180+
- F(3) - 1 times
181+
182+
For optimization memoization method time complexity, we can storing the previous two numbers only
183+
because that is all we need to get the next Fibonacci number in series.
184+
185+
JavaScript iterative implementation
186+
187+
```js
188+
189+
/**
190+
* Iterative
191+
* Time complexity: O(n)
192+
* Space complexity: O(1)
193+
*
194+
* @param number N
195+
* @return number
196+
*/
197+
function getFibonacciNumberIterative(N) {
198+
if (N <= 1) {
199+
return N;
200+
}
201+
let a = 0;
202+
let b = 1;
203+
let sum = null;
204+
205+
for (let i =2; i <= N; i++) {
206+
sum = a + b;
207+
a = b;
208+
b = sum;
209+
}
210+
211+
return b;
212+
}
213+
```
214+
215+
Alternative optimization recursion version, can be implementation of [tail recursion](https://en.wikipedia.org/wiki/Tail_call).
216+
Calculate the results first, and pass the results of your current call onto the next recursive call.
217+
218+
Tail recursion JavaScript implementation.
219+
220+
```js
221+
222+
/**
223+
* Time complexity: O(n)
224+
* Space complexity: O(n)
225+
*
226+
* @param number N
227+
* @return number
228+
*/
229+
function getFibonacciNumberTailRecursion (n, a = 0, b = 1){
230+
if (n > 0) {
231+
return fib(n - 1, b, a + b)
232+
}
233+
return a
234+
}
235+
236+
```
237+
238+
In the tail-recursive case, with each evaluation of the recursive call, the running total is updated.
239+
240+
#### Bonus reference.
241+
242+
Faster Math solution, not related to Dynamic programming.
243+
244+
```js
245+
/**
246+
* Time complexity: O(1)
247+
* Space complexity: O(1)
248+
*
249+
* http://www.maths.surrey.ac.uk/hosted-sites/R.Knott/Fibonacci/fibFormula.html
250+
* @param number N
251+
* @return number
252+
*/
253+
function getFibonacciNumberMath (N){
254+
return parsetInt(Math.round(Math.pow((1 + Math.sqrt(5)) / 2, N) / Math.sqrt(5)));
255+
}
256+
```
257+
258+
Formula [reference](http://www.maths.surrey.ac.uk/hosted-sites/R.Knott/Fibonacci/fibFormula.html).
259+

0 commit comments

Comments
 (0)