RECURSION FOR THE REST OF US
CS FUNDAMENTALS SERIES
http://bit.ly/29zaKm3
WHAT IS RECURSION?
THIS IS RECURSION.
RECURSION IS JUST A FUNCTION THAT CALLS
ITSELF.
WHY WOULD A FUNCTION CALL ITSELF?
A RECURSIVE FUNCTION CALLS ITSELF BECAUSE
IT’S RECURSIVE.
SERIOUSLY. WHEN WOULD YOU WANT
TO USE RECURSION?
NAMELY: WHEN A PROBLEM CAN BE
EASILY DEFINED IN TERMS OF SUBPROBLEMS.
QUINTESSENTIAL EXAMPLE:FACTORIAL!
5! = 5 * 4 * 3 * 2 * 1
4! = 4 * 3 * 2 * 1
5! = 5 * 4 * 3 * 2 * 1
You can easily express factorial in terms of subproblems.
Specifically, if you have the subproblem
(N - 1)! you can easily get
N!
N! = N * (N - 1)!
ONLY ONE PROBLEM…
WE NEED TO TELL THE SNAKE WHEN TO BITE DOWN
AND STOP RECURSING.
LET’S JUST SAY: 0! = 1
1! = 1 * 0!
1! = 1 * 1
SO WITH THAT, 1! = 1
2! = 2 * 1! 3! = 3 * 2! 4! = 4 * 3! 5! = 5 * 4!
= 2= 6= 24= 120
WE NEEDED TWO THINGS TO GET THE RECURSIVE
ALGORITHM.
N * (N - 1)!
First, we need to know how to solve the problem of size N in terms of smaller subproblems.
This is also called the inductive step.
(See: induction)
0! = 1 Second, we need the base case.
The recursion has to end somewhere, so we just hard-code it in.
AND THAT’S ALL THERE IS TO IT.
WORDS OF WARNING
▸ Recursion is often weird and unintuitive.
▸ Don’t try to do something recursively that’s better done iteratively!
▸ But there are times when it’s the most elegant way to solve a problem.
▸ E.g., factorial, sorting, many tree and graph problems (or generally when using any recursive data structure).
DEBUGGING RECURSION
▸ Recursion is usually more difficult to debug.
▸ If you don’t define your base case properly, you’ll usually trigger a stack overflow.
DEBUGGING RECURSION
▸ Console.log is your friend.
▸ Recursion usually works not at all, or everything works at once. This makes it trickier to introspect.
▸ Consider using: if (condition) debugger;
SIMPLE EXAMPLE function sum(arr)
MORE RECURSION ADVICE
▸ Start with the inductive step.
▸ Don’t go straight into coding it, first map it out and make sure you understand it.
▸ Then enter in the base case.
▸ Usually if you’re hard-coding multiple base cases, you’re overcomplicating it.
TIPS ON GENERATING THE INDUCTIVE STEP
▸ First, think about what a subproblem would look like. How can I make the input smaller?
▸ Is it size N - 1? N - 3? N / 2? All of these are valid subproblem sizes; what would make the solution easy?
▸ Think of recursion like cheating…
PRETEND YOU SMUGGLED IN AN ALGORITHM THAT ALREADY
SOLVES THE PROBLEM
BUT ONLY FOR SIZE N - 1…
HOW WOULD YOU USE THAT ALGORITHM TO GENERATE THE ANSWER FOR SIZE N?
FINALLY, JUST HARD-CODE IN A BASE CASE.
TA-DA! SPECS PASSING.
BASICALLY, RECURSION IS MAGIC.
HARDER EXAMPLES function triplets(arr)
function join(strings, separator)
OKAY, LET’S DO SOME RECURSION.
SO WHAT’S THIS MAXIMUM CALL STACK
SIZE EXCEEDED
BUSINESS?
FIRST, WE NEED TO UNDERSTAND HOW
FUNCTION CALLING WORKS.
fn:one line 17LocalVariablesTHE STACK:
fn:one line 17LocalVariables
THE STACK:fn:two line 22Local
Variables
fn:one line 17LocalVariables
THE STACK:fn:two line 22Local
Variables
fn:three line 27LocalVariables
>> Finished function 3.
fn:one line 17LocalVariables
THE STACK:fn:two line 22Local
Variables
fn:three line 28LocalVariables
fn:one line 17LocalVariables
THE STACK:fn:two line 22Local
Variables
fn:three line 28LocalVariables
fn:one line 17LocalVariables
fn:two line 22LocalVariables
fn:three line 28LocalVariables
NOW WE POP FROM THE STACK.
fn:one line 17LocalVariables
fn:two line 22LocalVariables
fn:three line 28Local
Variables
fn:one line 17LocalVariables
THE STACK:fn:two line 22Local
Variables
fn:one line 17LocalVariables
THE STACK:fn:two line 23Local
Variables
>> Done with function 2.
fn:one line 17LocalVariables
THE STACK:
fn:twoline 23
LocalVariables
fn:one line 17LocalVariables
THE STACK:
fn:one line 17LocalVariables
THE STACK:
>> And done with function 1!
SO HOW CAN WE BREAK THIS?
fn:one line 17LocalVariables
fn:two line 22LocalVariables
fn:three line 28LocalVariables
THE STACK:
fn:one line 17LocalVariables
fn:one line 17LocalVariables
fn:two line 22LocalVariables
fn:three line 28LocalVariables
THE STACK:
fn:one line 17LocalVariables
fn:two line 22LocalVariables
fn:one line 17LocalVariables
fn:two line 22LocalVariables
fn:three line 28LocalVariables
THE STACK:
fn:one line 17LocalVariables
fn:two line 22LocalVariables
fn:three line 28LocalVariables
fn:one line 17LocalVariables
fn:two line 22LocalVariables
fn:three line 28LocalVariables
THE STACK:
fn:one line 17LocalVariables
fn:two line 22LocalVariables
fn:three line 28LocalVariables
fn:one line 17LocalVariables
EVENTUALLY, WE RUN OUT OF MEMORY
(OR SPACE ON THE STACK).
THIS IS WHY RECURSIVE ALGORITHMS CAN’T
INFINITE LOOP*
* Without tail recursion or explicit loops
AND THIS IS ALSO WHY RECURSION IS SOMETIMES LESS
SPACE-EFFICIENT THAN ITERATION
EACH STACK FRAME HAS TO BE HELD IN MEMORY
AND GETS EVALUATED ONE BY ONE.
IT’S ALSO TRUE THAT EVERY RECURSIVE ALGORITHM
IMPLICITLY USES A STACK
SO IF YOU WANT TO IMPLEMENT A RECURSIVE ALGORITHM
ITERATIVELY, YOU OFTEN NEED TO MANAGE YOUR OWN STACK.
SO HOW DO YOU CALCULATE THE BIG O OF A RECURSIVE ALGORITHM?
Number of stack frames
*
average runtime per stack frame
IT GETS MORE COMPLICATED THAN THAT, BUT THAT’S A GOOD STARTING POINT.(SEE MASTER METHOD, RECURRENCES,
RECURSION TREES, ETC.)
DEMO RECURSIVE RUNTIME ANALYSIS
I AM HASEEB QURESHI
You can find me on Twitter: @hosseeb
You can read my blog at: haseebq.com
PLEASE DONATE IF YOU GOT SOMETHING OUT OF THIS
<3
Ranked by GiveWell as the most
efficient charity in the world!