CS107 - Lab 9

Java Programming (Recursion)

In this lab you will work on a problem that shows up frequently in practice, for instance in Vision and Robotics: counting cells in a blob. This problem is a good illustration of the power of recursion.Its solution is relatively easy to write recursively but it would be much more difficult to write an iterative solution.

Have fun!!


Counting Cells in a Blob

We have a two dimensional grid of cells, each of which may be filled or empty. Filled cells which are connected form what is called a blob. There may be several blobs on a grid. The problem is to write a recursive routine which returns as output the size of the blob containing a specified cell.

For example:

	(* denotes a filled cell)

		4 * *
                3   * *
             y  2     *   *
                1 *       * 
                0   *   * *
                  0 1 2 3 4
                      x


	For the above figure

	checkThisBlob called at x=1 y=3 should return a value of 5
	checkThisBlob called at x=0 y=1 should return a value of 2
	checkThisBlob called at x=4 y=4 should return a value of 0
	checkThisBlob called at x=4 y=0 should return a value of 4

Your task in this lab is to write a recursive method checkThisBlob.

Algorithm

  1. What instance(s) of the problem can serve as the base case(s)?

    For any particular cell (x, y) there are three possibilities.

    In the first two cases the size of the blob containing the cell(x, y) is zero because there is no blob. For the last case however further computations will be needed and so 1 and 2 are our base cases.

  2. If the cell (x, y) is on the grid and filled, how do we define the problem in terms of one or more smaller problems of the same type?

    We must define the size of the blob containing the filled cell (x, y) in terms of the size of one or more smaller blobs. If we mark the filled cell (x, y) empty the moment we visit it then we can redefine the problem as follows:

  3. CheckThisBlob should work conceptually as follows:
        if (the cell (x, y) is outside the grid) then the count is 0
    
        else if (the cell (x, y) is EMPTY) then the count is 0
    
        else if (the cell (x, y) is FILLED) then
    
           mark the cell (x, y) as EMPTY; the count is 1 + the counts of
           the cell (x, y)'s eight neighbours.  
    

  4. As the problem size diminishes will you reach the base cases?

  5. Everytime the routine visits a filled cell it marks it empty BEFORE it visits its neighbours, reducing the size of the blob by one. Eventually all of the filled cells in the blob will be marked EMPTY and the routine will encounter nothing but base cases.

  6. How is the solution from the smaller problem used to build a correct solution to the current larger problem ?

  7. The sum of all the values returned from the eight recursive calls to the neighbour cells is the size of the blob containing the current cell.

Notes

A side effect of the checkThisBlob routine is that all the elements of the blob containing the cell (x, y) are set to EMPTY. In other words the blob is erased.

If the cell visited is off the grid[][] or EMPTY, checkThisBlob returns a 0 immediately. Otherwise the recursive step executes.

checkThisBlob calls itself eight times, each time a different neighbour of the current cell is visited. The cells are visited in a clockwise manner starting with the neighbour above and to the left.

Common Errors

The sequence of statements executed in the routine checkThisBlob is very important.

The routine checkThisBlob() tests whether the cell (x, y) is on the grid before testing if it is empty. If the order were reversed then the condition ( grid[x][y] == EMPTY ) would reference an out-of-range element whenever the cell (x, y) was off the grid.

The statement grid[x][y] = EMPTY is used to set up conditions that will help solve a smaller version of the same problem and consequently preceeds the recursive calls.

If this statement was not executed first, then cell (x, y) would be counted more than once since it is a neighbour of each of its eight neighbours. In fact a much worse problem would occur. When each neighbour of the cell (x, y) is visited, checkThisBlob() is called again with the coordinates of the cell (x, y) as arguments. Thus if the cell (x, y) were still FILLED the recursive step would be executed erroneously and an infinite sequence of calls would be generated.

Related Problems

A side effect of the checkThisBlob routine is that the blob is erased from the array grid[][].

2-dimensional arrays

For this problem you will use a new type, a 2D-array. This is a type that stores an array of arrays, essentially a matrix, or a grid.

//declares a 2D array of 3 rows and 5 columns. The rows and columns are
//numbered starting at 0. 
int a [3][5]; 
In the example above there will be three rows, numbered 0, 1 and 2; and 5 columns, numbered 0, 1, 2, 3 and 4.

To access an element in a 2D array you need to specify two indices: the row, and the column. For example, a[0][0] gives the element in the 0th row and 0th column; a[1][3] gives the element in the 1st row and 3rd column, and so on.

We can assign values to a 2D array by assigning values to each element in part, with a loop, or at initialization, as follows:

int a [3][5] = {
   {0, 2,3,7,8},
   {4,7,1,2,3},
   {6,1,1,3,5} 
}; 
In the example above, a[0][0] = 0, a[0][1] = 2, a[0][2] = 3, a[0][3]=7, a[0][4]=8, a[1][0] = 4, a[2][0] = 6, a[2][4] = 5. It is easiest if you visualize a 2D array as a matrix, rather than row and column representing vertical and horizontal.

What to Do

A skeleton of the program is given here: BlobCheck.java. Take a look at it.

Your main program makes a copy of grid[][] and passes it, along with the parameters x and y, to checkThisBlob. blobSize should return the value computed by checkThisBlob as its own result. A call to checkThisBlob will now not result in erasure of a blob on the original grid. This is the usual procedure to handle the aforementioned problem. Placing the copy operation at the beginning of our recursive routine would lead to the creation of a new grid on every recursive call - a totally unnecessary and very expensive waste of computing time and space. This is the single most crucial step in this lab!

Your program should simply output the values for each square in the grid.

The hardest part in this lab will be to debug your code. Feel free to use a different (smaller) grid to help with the process.



What to turn in:

Send me only the .java files (not the entire folder), all in the same email, as attachments.