adding lab 8
This commit is contained in:
158
labs/08_recursion/README.md
Normal file
158
labs/08_recursion/README.md
Normal file
@@ -0,0 +1,158 @@
|
|||||||
|
# Lab 8 — Recursion
|
||||||
|
|
||||||
|
This lab gives you practice in the use of recursion to solve problems. All three checkpoints addressed in
|
||||||
|
this lab deal with finding and counting the number of paths between points on a rectilinear grid. A starting
|
||||||
|
point (x, y) with non-negative integer coordinates is provided. You are only allowed to move horizontally
|
||||||
|
and vertically along the grid. Hence, from (x, y) you may move to (x + 1, y), (x − 1, y), (x, y − 1), (x, y + 1).
|
||||||
|
Your goal is to return to the origin (0, 0) in such a way that you never increase the distance to the origin.
|
||||||
|
The distance is counted as the minimum number of total vertical and horizontal steps to reach the origin. In
|
||||||
|
the first checkpoint the grid will be “free”. In the second and third checkpoints, some of the grid locations
|
||||||
|
will be “blocked” in the sense that you can not move to that point.
|
||||||
|
|
||||||
|
Stop now and play with small examples. Draw pictures of a grid. Think about the implications of the rules
|
||||||
|
before you proceed with the checkpoints.
|
||||||
|
|
||||||
|
## Checkpoint 1
|
||||||
|
*estimate: 10-30 minutes*
|
||||||
|
Did you notice that the rules prevent certain moves from occurring? What are they? If you don’t get them
|
||||||
|
right you will not be able to do the lab correctly. Confirm your understanding with one of the lab TAs.
|
||||||
|
|
||||||
|
Now, write a simple recursive program (from scratch) that reads a starting location, (x, y) and returns the
|
||||||
|
total number of different paths back to the origin when the entire grid is “free”. Two paths are different if
|
||||||
|
there is at least one step on the path that is different even if most of the steps are the same. As a hint, when
|
||||||
|
x == 0 and y == 0 there is 1 path, when x == 2 and y == 1 there are 3 paths, and when x == 2 and
|
||||||
|
y == 2 there are 6 paths. When you’re confident your program is debugged try even bigger values. For what
|
||||||
|
values does the program take a few seconds to complete on your computer? If you increase these values by
|
||||||
|
1, how does it impact the running time? Is this what you expected from Big O Notation?
|
||||||
|
|
||||||
|
**To complete this checkpoint** show a TA your program to count all paths back to the origin. Be prepared
|
||||||
|
to discuss the running time of your program for different input values.
|
||||||
|
|
||||||
|
## Checkpoint 2
|
||||||
|
*estimate: 20-40 minutes*
|
||||||
|
|
||||||
|
Please download the files:
|
||||||
|
[start.cpp](start.cpp)
|
||||||
|
[grid1.txt](grid1.txt)
|
||||||
|
[grid2.txt](grid2.txt)
|
||||||
|
[grid3.txt](grid3.txt)
|
||||||
|
[grid4.txt](grid4.txt)
|
||||||
|
|
||||||
|
Starting from the supplied program, start.cpp, write a program to count the number of paths when some
|
||||||
|
of the grid locations are blocked. When a location is blocked, no path can go through it. Before writing
|
||||||
|
your own code, compile and run start.cpp. Use the files grid1.txt, etc. as input. These have a sequence
|
||||||
|
of blocked locations, ending with the point location (0, 0) (which isn’t blocked, but signals the end of the
|
||||||
|
input). The next location is the location for the initial (x, y). By changing this location you will be able to
|
||||||
|
test your program in different ways.
|
||||||
|
|
||||||
|
**To complete this checkpoint** show a TA your code to find and count legal (unblocked) paths.
|
||||||
|
|
||||||
|
## Checkpoint 3
|
||||||
|
*estimate: 30-40 minutes*
|
||||||
|
|
||||||
|
Modify your program from Checkpoint 2 so that it finds and outputs a legal path from the given (x, y)
|
||||||
|
location to the origin. You should both print the sequence of coordinates of a solution path AND modify the
|
||||||
|
print_grid function to draw the grid a second time with the path marked on the grid with ’$’ symbols. If
|
||||||
|
there is more than one path, it does not matter which it outputs. Did you notice that all legal paths to the
|
||||||
|
origin are the same length under the rules provided?
|
||||||
|
Now let’s explore recursion with gdb/lldb. <!--Review the handout from Lab 3, Checkpoint 3:
|
||||||
|
http://www.cs.rpi.edu/academics/courses/spring23/csci1200/labs/03_debugging/lab_post.pdf-->
|
||||||
|
Here’s a table of equivalent commands in gdb (Linux, WSL) vs. lldb (Mac, Linux, WSL):
|
||||||
|
https://lldb.llvm.org/lldb-gdb.html
|
||||||
|
1. Compile your program with the -g flag, so it includes debug info, including line numbers:
|
||||||
|
|
||||||
|
```console
|
||||||
|
clang++ -Wall -g start.cpp -o start.out
|
||||||
|
```
|
||||||
|
|
||||||
|
2. Start gdb (or lldb):
|
||||||
|
|
||||||
|
```console
|
||||||
|
gdb ./start.out
|
||||||
|
```
|
||||||
|
|
||||||
|
3. Set a breakpoint at the first line of the main function:
|
||||||
|
|
||||||
|
```console
|
||||||
|
b main
|
||||||
|
```
|
||||||
|
|
||||||
|
4. Now run/launch the program. Note: Here’s where we specify the necessary command line arguments.
|
||||||
|
|
||||||
|
```console
|
||||||
|
r grid2.txt
|
||||||
|
```
|
||||||
|
|
||||||
|
5. Place a breakpoint on the line in main where you first call your recursive function. Replace <NUM> with
|
||||||
|
the line number in your source code.
|
||||||
|
|
||||||
|
```console
|
||||||
|
b <NUM>
|
||||||
|
```
|
||||||
|
|
||||||
|
gdb will confirm with a message like this:
|
||||||
|
Breakpoint 2 at 0xwhatever: file start.cpp, line <NUM>
|
||||||
|
You can review your currently set breakpoints:
|
||||||
|
|
||||||
|
```console
|
||||||
|
info b
|
||||||
|
```
|
||||||
|
|
||||||
|
If you happen to mistype the line number, you can delete the breakpoint and try again. <BNUM> is the
|
||||||
|
breakpoint number with the incorrect line number.
|
||||||
|
|
||||||
|
```console
|
||||||
|
delete <BNUM>
|
||||||
|
```
|
||||||
|
|
||||||
|
6. Place another breakpoint at the first line of your recursive function. And then let the program run
|
||||||
|
until it reaches the next breakpoint by typing:
|
||||||
|
continue
|
||||||
|
When gdb pauses, it should inform you of the current program line number.
|
||||||
|
7. Now let’s step into your recursive function to get a closer look at recursion. Let the program run until
|
||||||
|
it enters the first recursive function call:
|
||||||
|
|
||||||
|
```console
|
||||||
|
continue
|
||||||
|
```
|
||||||
|
|
||||||
|
Inspect the data in the function arguments and the grid of booleans. Print the coordinates of the
|
||||||
|
current location, which are passed in as function arguments named x and y (for example).
|
||||||
|
|
||||||
|
```console
|
||||||
|
print x
|
||||||
|
print y
|
||||||
|
```
|
||||||
|
|
||||||
|
If you are using grid2.txt as your grid, these values should read x = 9 and y = 7.
|
||||||
|
8. Let’s navigate within our recursive algorithm using step and next. NOTE: Though similar sounding,
|
||||||
|
these are two different commands in gdb/lldb. step will enter into the details of a called function and
|
||||||
|
allow you to walk through its code, while next will fully execute one line from the current function
|
||||||
|
(skipping over all of the details of the function as it’s executed).
|
||||||
|
Use the next command to move down until we hit our next recursive call. Once on this line, let’s step
|
||||||
|
into the function call. After you step into the function, you may want to type continue so we can skip
|
||||||
|
to the next breakpoint. Print out the coordinates of the current location, which should be different.
|
||||||
|
Also, let’s print the boolean values from locations in the grid adjacent to the current position. For
|
||||||
|
example:
|
||||||
|
|
||||||
|
```console
|
||||||
|
print blocked_grid
|
||||||
|
print blocked_grid[x][y]
|
||||||
|
print blocked_grid[x-1][y+1]
|
||||||
|
```
|
||||||
|
Because gdb supports using basic math on these variables, we can also print the grid values above,
|
||||||
|
below, to the left, and to the right of our current position.
|
||||||
|
9. Use step and continue to go further and further into the recursion. Use backtrace to show the
|
||||||
|
function calls currently on the call stack, including the specific line numbers. As you walk step by step
|
||||||
|
through your algorithm and print data, do the variable values and sequence of locations and function
|
||||||
|
calls match your expectations? Ask a TA or mentor if you have questions about this information.
|
||||||
|
10. Finally, delete all of the breakpoints and then let the program finish without interruption.
|
||||||
|
|
||||||
|
```console
|
||||||
|
continue
|
||||||
|
```
|
||||||
|
|
||||||
|
**To complete this checkpoint and the entire lab**, present your modified program to a TA or mentor.
|
||||||
|
Demonstrate your skills with gdb/lldb to print out values of adjacent positions in the blocked grids vector,
|
||||||
|
to use backtrace, and navigate within recursive function calls using next, step, continue. Be prepared to
|
||||||
|
discuss how you will use gdb/lldb to help debug your future Data Structures homeworks.
|
||||||
7
labs/08_recursion/grid1.txt
Normal file
7
labs/08_recursion/grid1.txt
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
1 0
|
||||||
|
2 1 2 3
|
||||||
|
3 3 3 4
|
||||||
|
5 5
|
||||||
|
|
||||||
|
0 0
|
||||||
|
4 4
|
||||||
11
labs/08_recursion/grid2.txt
Normal file
11
labs/08_recursion/grid2.txt
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
2 0 3 1
|
||||||
|
0 1 3 3
|
||||||
|
1 2 4 1 5 2
|
||||||
|
1 3 5 3
|
||||||
|
0 5 2 5
|
||||||
|
5 4 6 4 7 4
|
||||||
|
3 5 4 6 5 6 7 5
|
||||||
|
7 7 8 8
|
||||||
|
|
||||||
|
0 0
|
||||||
|
9 7
|
||||||
28
labs/08_recursion/grid3.txt
Normal file
28
labs/08_recursion/grid3.txt
Normal file
@@ -0,0 +1,28 @@
|
|||||||
|
0 1
|
||||||
|
2 2
|
||||||
|
2 3
|
||||||
|
3 3
|
||||||
|
4 3
|
||||||
|
3 1
|
||||||
|
1 4
|
||||||
|
5 5
|
||||||
|
6 6
|
||||||
|
1 9
|
||||||
|
1 8
|
||||||
|
1 7
|
||||||
|
3 7
|
||||||
|
2 7
|
||||||
|
3 10
|
||||||
|
7 7
|
||||||
|
7 8
|
||||||
|
7 6
|
||||||
|
7 5
|
||||||
|
7 4
|
||||||
|
7 3
|
||||||
|
8 3
|
||||||
|
9 3
|
||||||
|
10 3
|
||||||
|
|
||||||
|
0 0
|
||||||
|
10 10
|
||||||
|
|
||||||
41
labs/08_recursion/grid4.txt
Normal file
41
labs/08_recursion/grid4.txt
Normal file
@@ -0,0 +1,41 @@
|
|||||||
|
0 1
|
||||||
|
2 2
|
||||||
|
2 3
|
||||||
|
3 3
|
||||||
|
4 3
|
||||||
|
3 1
|
||||||
|
1 4
|
||||||
|
5 5
|
||||||
|
6 6
|
||||||
|
1 9
|
||||||
|
1 8
|
||||||
|
1 7
|
||||||
|
3 7
|
||||||
|
2 7
|
||||||
|
3 10
|
||||||
|
7 7
|
||||||
|
7 8
|
||||||
|
7 6
|
||||||
|
7 5
|
||||||
|
7 4
|
||||||
|
7 3
|
||||||
|
8 3
|
||||||
|
9 3
|
||||||
|
10 3
|
||||||
|
15 15
|
||||||
|
17 15
|
||||||
|
16 15
|
||||||
|
18 15
|
||||||
|
14 15
|
||||||
|
13 15
|
||||||
|
12 14
|
||||||
|
17 18
|
||||||
|
7 18
|
||||||
|
7 17
|
||||||
|
7 16
|
||||||
|
7 15
|
||||||
|
7 14
|
||||||
|
|
||||||
|
0 0
|
||||||
|
18 18
|
||||||
|
|
||||||
130
labs/08_recursion/start.cpp
Normal file
130
labs/08_recursion/start.cpp
Normal file
@@ -0,0 +1,130 @@
|
|||||||
|
// Starting code for Checkpoints 2 and 3. This includes
|
||||||
|
// functions to read the grid and to output it.
|
||||||
|
|
||||||
|
#include <fstream>
|
||||||
|
#include <iostream>
|
||||||
|
#include <list>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
|
||||||
|
// A simple class to represent a point location. It only has a
|
||||||
|
// constructor and a two public member variables. This is one of the
|
||||||
|
// few times that you are allowed to use non-private member variables.
|
||||||
|
|
||||||
|
class Point {
|
||||||
|
public:
|
||||||
|
Point(int x0, int y0) : x(x0), y(y0) {}
|
||||||
|
int x,y;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
// NOTE: We could use a boolean (true/false) to represent whether each
|
||||||
|
// cell of the grid was blocked or open. This would be the minimal
|
||||||
|
// representation for memory.
|
||||||
|
|
||||||
|
// However, debuggers (both traditional and memory debuggers) might
|
||||||
|
// not be able to help debug errors with vectors of booleans, if they
|
||||||
|
// are efficiently packed by a clever STL implementation. So instead
|
||||||
|
// we use an enum, to improve readability, and to ensure that the
|
||||||
|
// status of each grid cell is stored as an integer avoiding debugger
|
||||||
|
// confusion during development.
|
||||||
|
|
||||||
|
|
||||||
|
enum GRID_STATUS { GRID_CLEAR, GRID_BLOCKED };
|
||||||
|
|
||||||
|
|
||||||
|
// Input the grid and the start location. The input is a sequence of
|
||||||
|
// x y locations, terminated by x==0 and y==0. The last input, which
|
||||||
|
// follows 0 0 input, is the start location.
|
||||||
|
//
|
||||||
|
// The grid is represented as a 2d vector of GRID_STATUS, with each location
|
||||||
|
// that is blocked --- meaning that no path can go through --- being
|
||||||
|
// represented by the value GRID_BLOCKED. The grid is large enough to
|
||||||
|
// include all blocked points and include the starting location. The
|
||||||
|
// first coordinate of the vector of vectors is the x coordinate, and
|
||||||
|
// the second is the y coordinate. The format of the input is
|
||||||
|
// specified in the lab handout.
|
||||||
|
|
||||||
|
void read_grid(std::istream& istr,
|
||||||
|
std::vector<std::vector<GRID_STATUS> >& blocked_grid,
|
||||||
|
int& start_x, int& start_y) {
|
||||||
|
|
||||||
|
// Read the x y locations into a list of Points. Keep track of the
|
||||||
|
// max x and max y values so that the size of the grid can be
|
||||||
|
// determined.
|
||||||
|
int x, y;
|
||||||
|
int max_x = 0, max_y = 0; // keep track of the max coordinate values
|
||||||
|
std::list<Point> blocked_points;
|
||||||
|
while ((istr >> x >> y) && ! (x==0 && y==0)) {
|
||||||
|
blocked_points.push_back(Point(x,y));
|
||||||
|
if (x > max_x) max_x = x;
|
||||||
|
if (y > max_y) max_y = y;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Now that a 0 0 location has been read, read the start location.
|
||||||
|
// If this is beyond the max x or y value then update these values.
|
||||||
|
istr >> start_x >> start_y;
|
||||||
|
if (start_x > max_x) max_x = start_x;
|
||||||
|
if (start_y > max_y) max_y = start_y;
|
||||||
|
|
||||||
|
// Make a vector of vectors with all entries marked clear.
|
||||||
|
std::vector<GRID_STATUS> one_row_of_ys(max_y+1, GRID_CLEAR);
|
||||||
|
std::vector<std::vector<GRID_STATUS> > empty_grid(max_x+1, one_row_of_ys);
|
||||||
|
blocked_grid = empty_grid;
|
||||||
|
|
||||||
|
// For Point in the list, mark the location in the list as blocked.
|
||||||
|
std::list<Point>::iterator p;
|
||||||
|
for (p = blocked_points.begin(); p != blocked_points.end(); ++p) {
|
||||||
|
blocked_grid[p->x][p->y] = GRID_BLOCKED;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// Output the grid to cout. The form of the output is explained in
|
||||||
|
// the cout statement below.
|
||||||
|
|
||||||
|
void print_grid(const std::vector<std::vector<GRID_STATUS> > & blocked_grid,
|
||||||
|
unsigned int start_x, unsigned int start_y) {
|
||||||
|
|
||||||
|
std::cout << "Here is the grid with the origin in the upper left corner, x increasing \n"
|
||||||
|
<< "horizontally and y increasing down the screen. An 'X' represents a blocked\n"
|
||||||
|
<< "location and the 'S' represents the starting location.\n\n";
|
||||||
|
|
||||||
|
for (unsigned int y=0; y<blocked_grid[0].size(); ++y) {
|
||||||
|
for (unsigned int x=0; x<blocked_grid.size(); ++x) {
|
||||||
|
if (x == start_x && y == start_y)
|
||||||
|
std::cout << " S";
|
||||||
|
else if (blocked_grid[x][y] == GRID_BLOCKED)
|
||||||
|
std::cout << " X";
|
||||||
|
else
|
||||||
|
std::cout << " .";
|
||||||
|
}
|
||||||
|
std::cout << std::endl;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
int main(int argc, char* argv[]) {
|
||||||
|
if (argc != 2) {
|
||||||
|
std::cerr << "Usage: " << argv[0] << " grid-file" << std::endl;;
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
std::ifstream istr(argv[1]);
|
||||||
|
if (!istr) {
|
||||||
|
std::cerr << "Could not open " << argv[1] << std::endl;
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::vector<std::vector<GRID_STATUS> > blocked_grid;
|
||||||
|
int start_x, start_y;
|
||||||
|
read_grid(istr, blocked_grid, start_x, start_y);
|
||||||
|
print_grid(blocked_grid, start_x, start_y);
|
||||||
|
|
||||||
|
// Start here with your code...
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
Reference in New Issue
Block a user