CPSC 225 Intermediate Programming Spring 2025

Lab 10: Recursion

Due: Mon May 5 at 11:59pm

Introduction

The goal of this lab is to gain some experience with writing recursive subroutines, and to see several ways recursion comes up as a programming technique — implementing a recursive definition (Gray codes), divide and conquer (quadtree image compression), and recursive backtracking (voting power).

Part of this assignment is based on materials by Julie Zelenski.

About Due Dates

Note the due date: this lab is not at the start of lab a week later as usual!

To be eligible for revise-and-resubmit, you must turn in something by the due date. Late work and extensions are generally not accepted/granted; see the posted late policy for more information.

Note that there will be an abbreviated revised-and-resubmit for this assignment. There is not time to grade and return work for a resubmit, but there will be an opportunity to discuss your lab in office hours during reading period and resubmit after that. The resubmit will be due by the final end-of-semester deadline at the end of the final exam timeslot.

About Collaboration and Getting Help

You may work with a partner if you wish. Both partners must actively contribute to the solution! You only need one handin for the group — be sure to put both teammates' names in every file.

Otherwise you may get help but you may not use other resources (friends, neighbors, websites, ChatGPT, etc) to produce answers. See the posted policy on academic integrity for more information.

Handin

To hand in your work:


Exercises

Setup


Gray Code

The notion of a Gray code, or more specifically, a reflected binary Gray code was introduced in class: it is an ordering of numbers such that their binary representations vary by only one bit between successive numbers. Gray codes have a number of applications.

Write a program named Gray which generates n-bit Gray codes:

Gray codes were defined as follows in class:

     1-bit gray code: 0, 1
      
     n-bit gray code:
       generate (n-1)-bit gray code
       prepend 0 to each element in order
       prepend 1 to each element in reverse order
    

Refer back to the slides from class for examples of 1-, 2-, and 3-bit Gray codes generated using this definition.

To implement the gray method, translate the pseudocode definition given above. Refer to the schedule page for examples of translating other recursive definitions (factorial, Fibonacci) into code, including the structure of the recursive method with base case(s) and recursive case(s).


Quadtree Image Compression

Quadtree image compression an be used to reduce the complexity of image data by representing large, uniform-color regions with simple geometric shapes. It is especially well-suited for compressing black-and-white or grayscale images where large blocks of similar intensity can be efficiently grouped together but it can also be applied to color images as shown below. (The original image is on the left; the compressed version is on the right.)

The core idea is to divide and conquer: first check whether a region of the image has a uniform color — that is, whether all the pixels in the region are similar enough, according to some threshold. If they are, you can represent that entire region as a single rectangle with the average color of the pixels in the region. If not, divide the region into four equal quadrants (hence the name quadtree) and recursively apply the same strategy to each of the smaller subregions.

Then, instead of storing every individual pixel, the image can now be described using just the shapes and colors of the uniform blocks. This can lead to significant compression, especially for images with large areas of flat color, as well as improved rendering speed. The hierarchical structure of quadtrees also means that image is captured at multiple levels of detail.

A JavaFX main program and several utility routines have been provided in ImageCompression.java. Your task is to write methods to build a quadtree from an image and to draw the color blocks of the quadtree so the result of the compression can be seen. In particular:

Feel free to substitute your own image for the provided one (just make sure to import your image into your project so it gets handed in). Also feel free to experiment with the THRESHOLD value — it should be between 0 and 1 and defines how much variation in color is allowed within the notion of "uniform".

To build a quadtree for a region of an image:

  if the current region is a uniform color (within the threshold),
    create a node for the current region with the average color of the region as its color
    return that node

  otherwise
    divide the region into four quadrants and build the quadtree for each
    create a node for the current region with the four quadtrees as its children
    return that node

A math note: because the regions are specified with integer coordinates, careful handling is needed for odd-valued dimensions to avoid small gaps in the result. To divide a region with width w in half horizontally, use w/2 for the width of the left half and w-w/2 for the width of the right half. (Handle the height similarly.) Recall that if w is an int value, w/2 will round down (integer division).

Drawing the quadtree means traversing the tree, drawing a filled rectangle for each leaf of the tree. Note that QuadTreeNode has a fillRegion method that does the drawing part, so you'll just need a variation of binary tree traversal. In other words, to draw the subtree rooted at a node:

  if the node is a leaf
    use fillRegion to draw a filled rectangle

  otherwise
    for each child of the node
      draw the subtree rooted at that node

Voting Power

The Banzhaf power index is a measure of how much influence an individual voter has in a system where voters are not all equal. (This occurs in a block voting system, where each voter controls a certain number of votes that are allocated on an all-or-nothing basis.) An example is the US Electoral College - the 538 electoral votes are not divided equally among the states (e.g. California has 55 electoral votes and Vermont just 3). This means a big state like California is more likely to decide the election than a small state like Vermont. The Banzhaf power index considers the likelihood of a particular voter being the swing voter in a given election.

We'll consider only elections where there are just two possible votes: for candidate A or for candidate B, for a ballot measure or against it, etc. To count the number of times a particular block swings the election, consider all of the possible combinations of votes from the other blocks and, for each combination, determine whether or not the vote of the desired block would change the outcome of the election in that case.

For example, consider a simplified Electoral College with just four states: New York (29 votes), Ohio (18 votes), North Carolina (15 votes), and New Hampshire (4 votes). With a total of 66 votes, a majority of at least 34 votes is needed to win the election. To determine how often New Hampshire swings the election, consider all of the possible combinations of how the other three states could vote and then consider whether New Hampshire's vote has an effect on the outcome:

states, electoral votes, and how they vote vote totals winner if NH votes... is NH a swing state?
291815
NYOHNCDRDR

DDD620DDFALSE
DDR4715DDFALSE
DRD4418DDFALSE
DRR2933tieRTRUE
RDD3329DtieTRUE
RDR1844RRFALSE
RRD1547RRFALSE
RRR062RRFALSE

This shows that New Hampshire is a swing state in two of the possible scenarios. Similar calculations reveal that New York is a swing state in six scenarios and Ohio and North Carolina are each swing states in four scenarios. (Where would you spend your advertising money if you were a candidate?)

You've been provided with a main program VotingPower which reads in information about voting blocks and prints out how many times each block swings the election, indicating that block's voting power. A sample run: (user input in bold)

how many blocks? 4
enter votes for each block: 29 18 15 4

number of scenarios where a block swings the election:
block 0: 6
block 1: 4
block 2: 4
block 3: 2

Your task is to implement the swing scenario count:

To count the swing scenarios, we need to enumerate all the possible combinations of how different blocks vote. The structure of this is very similar to how n queens works — in that, queens are placed column-by-column and for each column, each of the possible rows where the queen could be placed are considered. In this case, instead of going through columns and considering the rows where a queen can be placed, countSwingScenarios goes through each of the voting blocks and considers how that current block could vote in the election — for candidate 0 or candidate 1. For each candidate alternative, the block's votes are given to that candidate and then recursion continues with the next block.

As with n queens, implementing this requires a helper method with extra parameters — we need to keep track of the board and the current column with n queens, and with this we need to keep track of the current tally of votes in the election and the current block who is voting. This current block is separate from the block parameter in countSwingScenarios — don't mix up those parameters! The body of the helper will do the following:

  if all of the blocks have voted
    if the specified block swings the election, return 1
    otherwise return 0

  otherwise if the current block is 'block'
    // ('block' is the one we are counting swings for - it doesn't vote at this point)
    recurse with the next block

  otherwise
    for each candidate
      allocate the current block's votes to that candidate
      recursively count the swing scenarios for the next block and add that to the total
      remove the current block's votes from that candidate
    return the total number of swing scenarios found

The public countSwingScenarios will create a new ElectionTally and call the helper for block 0 as the current block.