Recursive

CS 311 Structures and Lecture Slides Wednesday, September 25, 2019

Glenn G. Chappell Department of University of Alaska Fairbanks [email protected] © 2005–2019 Glenn G. Chappell Some material contributed by Chris Hartman Unit Overview Recursion & Searching

Major Topics P§ Introduction to recursion P§ Search algorithms I P§ Recursion vs. iteration P§ Search algorithms II P§ Eliminating recursion P§ Search in the ++ STL § Recursive backtracking

2019-09-25 CS 311 Fall 2019 2 Review Recursion vs. Iteration [1/4]

Choice of can make a huge difference in performance.

Computing F6 fibo_recurse.cpp fibo_first.cpp

8 function F6 F6 25 function calls calls F5 & F6 F4 F5

F4 & F5 F2 F3 F3 F4

F3 & F4 F0 F1 F1 F2 F1 F2 F2 F3

F2 & F3 F0 F1 F0 F1 F0 F1 F1 F2

F1 & F2 F0 F1 Fibonacci No. fibo_recurse.cpp fibo_first.cpp F0 & F1

F7 9 calls 41 calls F-1 & F0 F10 12 calls 177 calls

F20 22 calls 21,891 calls

F40 42 calls 331,160,281 calls

2019-09-25 CS 311 Fall 2019 3 Review Recursion vs. Iteration [2/4]

A running program uses a call stack, which holds stack frames. § Each stack frame corresponds to an invocation of a function. It holds automatic variables and the function’s return address. § A function call pushes a new stack frame on the top of the stack. § When the function exits, this stack frame is popped off the stack. § Recursion results in the call stack holding multiple stack frames that correspond to different invocations of the same function.

zebra void zebra(int n) Stack return address Frame { n: 0 if (n == 0) zebra Call Stack return address … Stack Frame n: 1 … zebra zebra(n-1); Stack return address Frame } n: 2

2019-09-25 CS 311 Fall 2019 4 Review Recursion vs. Iteration [3/4]

A function call’s recursion depth is the greatest number of stack frames on the call stack at any one time as a result of the call.

fibo_recurse.cpp fibo_first.cpp

Recursion depth: 8 Recursion depth: 6 F6 F6

F5 & F6 F4 F5

F4 & F5 F2 F3 F3 F4

F3 & F4 F0 F1 F1 F2 F1 F2 F2 F3

F2 & F3 F0 F1 F0 F1 F0 F1 F1 F2

F1 & F2 F0 F1

F0 & F1

F-1 & F0 When analyzing time usage, the total number of calls is of interest. When analyzing space usage, the recursion depth is of interest.

2019-09-25 CS 311 Fall 2019 5 Review Recursion vs. Iteration [4/4]

Two factors can make recursive algorithms inefficient. § Inherent inefficiency of some recursive algorithms § But there are efficient recursive algorithms. § Function-call overhead These two are § Making all those function calls requires work: important pushing and popping stack frames, saving regardless of the return addresses, creating and recursive destroying automatic variables. algorithm used.

And recursion has another problem. § Memory-management issues § A high recursion depth causes the system to run out of memory for the call stack. This is stack overflow, and it generally cannot be dealt with using normal error-handling procedures. The result is usually a crash. § When we use iteration, we can manage memory ourselves. This can be more work for the programmer, but it also allows proper error handling.

2019-09-25 CS 311 Fall 2019 6 Review Search Algorithms I/II [1/3]

The Binary finds a given key in a sorted list. § Here, key = thing to search for. Often there is associated data. § In computing, sorted means in (some specified) order. Procedure § Pick an item in the middle of the list: the pivot. § Compare the given key with the pivot. § Using this, narrow search to top or bottom half of list. Recurse. Example: Use Binary Search to search for 64 in the following list.

Look for 64 in this list.

5 8 9 13 22 30 34 37 38 41 60 63 65 82 87 90 91

Pivot. Is 64 < 38? No. Recurse: look for 64 in this list …

2019-09-25 CS 311 Fall 2019 7 Review Search Algorithms I/II [2/3]

Sequential Search (also called ) is another algorithm for finding a given key in a list.

Procedure § Start from the beginning, looking at each item, in order. § If the desired key is found, then stop, answering YES. § If the end of the list is reached, then stop, answering NO.

5 8 9 13 22 30 34 37 38 41 60 63 65 82 87 90 91 …

2019-09-25 CS 311 Fall 2019 8 Review Search Algorithms I/II [3/3]

Binary Search is much faster than Sequential Search, so it can process much more data in the same amount of time.

Number of Look-Ups Maximum List Size: Maximum List Size: We Have Time For Binary Search Sequential Search 1 1 1 2 2 2 3 4 3 4 8 4 10 512 10 20 524,288 20 40 549,755,813,888 40 k Roughly 2k k

“The fundamental law of computer science: As machines become more powerful, the efficiency of algorithms grows more important, not less.” § Lloyd N. Trefethen

2019-09-25 CS 311 Fall 2019 9 Review Eliminating Recursion

It can sometimes be helpful to eliminate recursion—converting it to iteration.

We can eliminate recursion by mimicking the call stack. This method always works, but it is not necessarily a good idea. § We will discuss this method further when we cover Stacks.

If a recursive call is a tail call (the last thing a function does), then we have tail recursion.

Eliminating tail recursion is easy and practical.

See binsearch2.cpp, binsearch3.cpp, binsearch4.cpp.

2019-09-25 CS 311 Fall 2019 10 Review Search in the C++ STL

The STL includes four function templates that do Binary Search: § std::binary_search § std::lower_bound § std::upper_bound § std::equal_range All are called the same way, but they return different information. They allow for (optional) specification of a custom comparison.

The STL also includes Sequential Search: § std::find

Some STL containers, like std::map, have their own search-by-key functionality, in the form of a member function find. § More on this when we cover Tables.

2019-09-25 CS 311 Fall 2019 11 Recursive Backtracking Basics — Backtracking

Now we discuss a different kind of search.

In most of the programming you have done, you have probably proceeded directly toward our goal. § Work never had to be undone. § But what if it does?

Sometimes we search for a solution to a problem. § Sometimes we need to undo some work and try something else. § Restoring to a previous state is called backtracking.

It is often convenient to implement backtracking using recursion. § However, such recursive programming can require rather different ways of thinking from the recursion we have discussed so far.

2019-09-25 CS 311 Fall 2019 12 Recursive Backtracking Basics — Partial Solutions

Recursive solution search works well when we have a notion of a partial solution. § Each recursive call says, “Look for full solutions based on this partial solution.” § The function attempts to build more complete solutions based on the partial solution it was given. § For each possible more complete solution, a recursive call is made. § We usually have a wrapper function, so that the client does not need to deal with partial solutions.

In a recursive solution search, to backtrack, we often simply return from a function.

2019-09-25 CS 311 Fall 2019 13 Recursive Backtracking Basics — No-Backtracking Diagram

In the recursion we studied earlier: § A recursive call is a request for information (or action). § The return sends the information back (if any). The diagram below shows the information flow in fibo_first.cpp.

Q. What is F ? 3 Client A. F3 = 2. Goal F3=? F3=2

fibo(3)

F1=? F2=1 F1=1 F2=?

fibo(1) fibo(2)

F0=? F1=1 F0=0 F1=?

fibo(0) fibo(1)

2019-09-25 CS 311 Fall 2019 14 Recursive Backtracking Basics — Backtracking Diagram

In recursive backtracking: § A recursive call means “continue with the proposed partial solution”. § Return means “backtrack”. The diagram illustrates a search for 3-digit sequences with digits in {0, 1}, in which no two consecutive digits are the same. On finding a solution, stop. Or continue, finding all solutions.

Each recursive okay? call handles a Yes. Continue. partial solution

Add 0 Add 1 0 okay? 1 okay? Yes. Continue. Yes. Continue.

Add 0 Add 1 Add 0 Add 1 0,0 okay? 0,1 okay? 1,0 okay? 1,1 okay? No. Yes. Continue. Yes. Continue. No.

Add 0 Add 1 Add 0 Add 1 0,1,0 okay? 0,1,1 okay? 1,0,0 okay? 1,0,1 okay? Yes. OUTPUT. No. No. Yes. OUTPUT.

Goal

2019-09-25 CS 311 Fall 2019 15 Recursive Backtracking n-Queens — Description

We now look at how to solve the n-Queens Problem. § Place n queens on an n × n chessboard so that none of them can attack each other.

Queen Can attack N, S, E, W Q 4×4 & 4 diagonals, chessboard any distance

Good Good BAD Q Q Q Q Q Q Q Q Q Q Q Q Q Q Q

2019-09-25 CS 311 Fall 2019 16 Recursive Backtracking n-Queens — How to Do It [1/4]

To Figure Out § What is a partial solution for the problem we wish to solve? How should we represent a partial solution? § If possible, we should represent a partial solution in a way that makes it convenient to determine whether we have a full solution. § It is also nice if we can quickly determine whether we have a dead end. § How should we output a full solution?

2019-09-25 CS 311 Fall 2019 17 Recursive Backtracking n-Queens — How to Do It [2/4]

Representing a Partial Solution Partial Representation § Number rows and columns 0 .. n–1. Solution § Two variables: 0 1 2 3 board n § board (vector of int). 0 Q 2 4 § n (int). 1 Q 0 § Variable n holds the number of 2 Q 3 3 Q 1 rows/columns in a full solution. § Variable board holds the columns of 0 1 2 3 board n queens already placed, one per row. 0 Q 2 4 § The size of variable board is the 1 Q 0 number of rows in which queens 2 3 have been placed. 0 1 2 3 board n 0 empty 4 1 2 3

2019-09-25 CS 311 Fall 2019 18 Recursive Backtracking n-Queens — How to Do It [3/4]

The Code § Nonrecursive wrapper function § Create an empty partial solution. § Call the workhorse function with this partial solution. § Recursive workhorse function is given a partial solution, prints all full solutions that can be made from it.

§ Do we have a full solution? This part might not be § If so, output it. necessary. Another way § Do we have a clear dead end? to handle dead ends is simply not to make any § If so, simply return. recursive calls when we § Otherwise: get to this part. § Make a recursive call for each way of extending the partial solution.

2019-09-25 CS 311 Fall 2019 19 Recursive Backtracking n-Queens — How to Do It [4/4]

Notes § We often need to check the validity of a proposed way to extend a partial solution. It can be convenient to have a separate function that does this checking. § When backtracking, we need to make sure we go back to the previous partial solution. Two ways to do this: § Each recursive call has its own copy of the current partial solution. § All use the same data. When backtracking, undo any changes made.

2019-09-25 CS 311 Fall 2019 20 Recursive Backtracking n-Queens — CODE

TO DO § Write a recursive function to print solutions to the n-Queens Problem. Done. See nqueen.cpp.

2019-09-25 CS 311 Fall 2019 21 Recursive Backtracking Counting Solutions — Diagram

We can also count solutions. Each recursive call returns the number of full solutions based on a given partial solution. § Base Cases § “Found a solution” returns 1. § “Dead end” returns 0. § Recursive Case § Make recursive calls, add their return values, and return the total.

Client 2 okay? Yes. Continue.

Add 0 1 1 Add 1 0 okay? 1 okay? Yes. Continue. Yes. Continue.

Add 0 0 Add 1 1 Add 0 1 Add 1 0 0,0 okay? 0,1 okay? 1,0 okay? 1,1 okay? No. Yes. Continue. Yes. Continue. No.

Add 0 1 Add 1 0 Add 0 0 Add 1 1 0,1,0 okay? 0,1,1 okay? 1,0,0 okay? 1,0,1 okay? Yes. No. No. Yes.

2019-09-25 CS 311 Fall 2019 22 Recursive Backtracking Counting Solutions — How to Do It

The Code § Nonrecursive wrapper function § Create an empty partial solution. § Call the workhorse function with this partial solution. § Return the return value of the workhorse function. § Recursive workhorse function is given a partial solution, returns the number of full solutions that can be made from it. § Do we have a full solution? § If so, then return 1. § Do we have a clear dead end? As before, this might be § If so, then return 0. unnecessary. § Otherwise: § total to zero. § For each way of extending the current partial solution, make a recursive call, and add its return value to total. § Return total.

2019-09-25 CS 311 Fall 2019 23 Recursive Backtracking Counting Solutions — CODE

TO DO § Modify the n-Queens code to count all possible non-attacking arrangements of n queens, instead of printing them.

Done. See nqueencount.cpp.

2019-09-25 CS 311 Fall 2019 24 Unit Overview & Sorting

Our next unit covers analyzing the efficiency of algorithms, along with a number of algorithms for sorting. Major Topics § § Introduction to Sorting § Comparison Sorts I § Asymptotic Notation § Divide and Conquer § Comparison Sorts II § The Limits of Sorting § Comparison Sorts III § Non-Comparison Sorts § Sorting in the C++ STL

The in-class Midterm Exam will be near the end of this unit.

2019-09-25 CS 311 Fall 2019 25