in this lesson we will learn an important
technique to improve the performance
of a recursive program
and this technique is called
recursion with memoization
now in a previous lesson we had shown to
you using the example off
Fibonacci sequence that why recursion 
is not always good
in terms of performance
Let's quickly have a recap of it
so Fibonacci sequence is defined by
this particular
recurrence relation the first two elements
in the sequence are zero and one and
all other elements are sum of
previous two elements
and if
we have to write a recursive
implementation of this particular
sequence
Let's say we want
to write a function
fib(n)
that'll give me
F(n) from the above sequence
it goes something like this if n is
less than or equal to one
then we simply return n else
we return Fib(n-1) plus 
Fib(n-2) so we make
two recursive calls 
first to calculate Fib(n-1)
and then to calculate Fib(n-2)
and sum them up
and return the value
now if i make a call to calculate
Fib(5)
and let's say i will write F(5) here
for shortcut
to Fib(5)
then this makes
recursive call first to calculate
F(4)
it also makes a call to calculate F(3)
but we will go
in the order in which the actual program
executes
so F(4) makes a call to F(3)
F(3) makes a call to F(2)
and we go on building this particular
structure
called recursion tree
so F(1) and F(0) do not make
any further recursive call
and this simply returns
one and zero respectively
this is a recursion tree
now to calculate F(5) we have 
calculated
F(2) thrice here
and similarly we have calculated F(3)
twice here
this is simply an overhead
or redundancy
and for higher values of n this
increases the time and space taken by
the program exponentially
so let's do something to avoid the re
calculation of the same state or the same
value
again and again so
i will modify the function here in the
left
a little bit
so i will try to write this function 
Fib(n) again
and let's start to the base condition
if n is less than or equal to
one we simply return n
what it basically means is that we already know
 
63
00:02:34,540 --> 00:02:36,159
F(0) and
F(1)
so anytime
we are asked for
F(0) and F(1) we simply return it
and do not make a recursive call
so let's say once we find F(2) or
calculate F(2)
for the first time
we save it in the memory
so that i do not re-calculate it again
similarly
any F(n) like F(3) F(4) and so on
once i calculate it for the first time
let's say i will save it in the memory so in the
program i will say something like if 
F(n)
is in memory
do not try to calculate it simply 
return it
so we simply return F(n) else
we will first
try to calculate
Fn
by making recursive calls
to calculate
Fib(n-1) and Fib(n-2)
and now i will first
save this Fn in memory so that i do
not have to re-calculate this again
and finally
i will simply return this Fn
we will show to you in an actual
program how to store all this
F(2) F(3) F(n) in the memory
and return it
as and when needed
now let's see how this program now
improves our previous recursion
say we again
want to calculate F(5)
and this makes a recursive call to F(4)
this makes a recursive call because we
do not have F(5) and F(4)
in memory yet
we have not finished calculating it
calculating them even once
F(4) again makes a call to calculate 
F(3)
F(3) makes a call to calculate F(2)
and F(2) makes a call to calculate 
F(1)
and F(1) does not make a recursive
call any further
so now at this stage
this F(2) has 
finished calculating ones
so now
we save it in the memory
so now F(3) also calculates
also makes a recursive call to F(1)
now after this returns
we also have
finished calculating F(3) one's so F(3) is
also in memory
now F(4) also makes a call to calculate
F(2) now
but F(2) is already in memory now
so this particular recursion tree does
not grow any further from here we
simply
return from here
because F(2) returns
the value
from the memory
again if F(5) also makes a recursive
call to
F(3)
now F(3) has also been calculated once
before and it is in memory so
F(3) simply returns the value from 
memory here
and the tree does not grow here as well
so what we're basically doing here 
is
we are avoiding all the re-calculation of
the same state
again and again
this particular implementation reduces
the time complexity
of this particular program
significantly
so this is recursion with memoization
let's now see in some real code how this
works
and i will write some C++ 
for you
so here we have a simple
recursive implementation of Fibonacci
sequence in C++
and we have this method Fib
this is the base condition
and this is the recurrence relation
and we asked the user for an n 
and print the result
after calling Fib(n)
so lets see what happens if we run this
program
let's say we first gave
n i equal to 10
this gives me the result fifty-five
immediately and now let's give a
slightly higher n like n is equal to
forty
and this time it's taking some time
it is still executing and i'm waiting for the
result
finally we get the result but it took
some, it took some time a couple of
seconds to give me the result now
let us try to write recursion with
memoization and see what good it does to
us
so what i will do is i will first  declare an array
F of size fifty one
and we will come back to
why we have
chosen the size fifty one only in the
main method we go ahead and say that 
for all i from
zero to
fifty
set F[i] is equal to minus one
so if F[i]
is minus one
we can say that
F(i) is not calculated yet
in the method
Fib(n)
we would say that
F[n] is not equal
to minus one then
definitely
it is in the memory and it is already
calculated
so we will simply return F[n]
else
we will calculate F[n] by 
this recurrence relation
and then return
F[n] now we have declared this particular array
in global scope
so that
only one copy of it is created and the
same copy is accessible to all the
calls of this method Fib and
method main as well
now let us run this program and see what
happens
let's try this again for n is equal to
ten
and we get in
result as fifty five which is correct
we also get the result in no time
for n is equal to forty
let see what happens
and this gives me the result immediately
so recursion with memoization in
this particular example
is not as efficient as an iterative
implementation in terms of memory
but it is as good as an iterative
implementation in terms of time
for larger values of n and it is a lot
better than simple recursion
which grows exponentially the time taken
by
which grows exponentially with the input
so a couple of things in the program
we have declared the array of size 
fifty one only
this limits us
to calculating only
the first fifty-one elements in the
Fibonacci sequence if we are using 
recursion with memoization you
definitely need to make a call in
recursion with memoization that what
is the maximum value
of input that you're giving and
accordingly you have to declare this
array we could also avoid writing this particular
condition here
we could simply go ahead and in the main method
we could initialize F[0]
as zero
and F[1] as one
and this particular condition if F[n] not equal
to minus one
will take care of the previous
condition that we have omitted
or removed the recursion with memoization
is a very nice technique algorithm
design and we will encounter more of
it
in coming lessons
so thanks for watching
