CSCI 151 - Lab 2 More Java Fun

10:00pm, Sunday, February 24

In this lab, you will be spending more time working with Java. The goals of this lab are to

Part 1 - Recursion

Our first task is to write some recursive problems in Java. If you took CS 150, these problems should look very familar to you. Feel free to consult your code from Lab 7 of 150 to see how you solved them using Python.

Create a single program called Recnum.java. This program will read in two non-negative numbers from the user and perform some computations with them. All computation must be done recursively: you may NOT use any loops or Math functions. In particular, your program should do the following (details on the functions are given further below):

  1. Prompt the user for an integer n. (Hint you may want to reread about Scanners from Lab 1)
  2. Prompt the user for an integer k.
  3. Compute n raised to the power of k.
  4. Compute the sum of the first n perfect squares.
  5. Compute the value of n choose k.
Note: All of your methods will need to be declared static so you can call them without creating a Recnum object.

Powers

n raised to the k is the product of k n's. 2 raised to the 3 is 8. 3 raised to the 2 is 9. What is -5 raised to the 4? Which is larger, 2 raised to the 100 or 100 raised to the 2?

Since we're trying to solve this recursively, we need to think about how exponentiation (raising one number to the power of another) can be described recursively. That is, we want to try to describe n raised to the k in terms of a smaller version of that same problem. The key insight is that [n raised to the k] can be described as simply n times [n raised to the (k-1)]. Note that we've now defined exponentiation in terms of a simple operation (a single multiplication). That's our recurrence. All that remains is to specify a base case so that our recursion know to stop at some point. We could define n raised to the 1 as n, but a better solution (in that it'll work for 0 as well) is to define n raised to the 0 as 1. Question: would it have made sense to define n raised to the k in terms of (n-1) raised to the k?

You now have the necessary pieces to write a recursive method public static int pow(int n,int k). Test that it works on a range of cases. Be sure to add comments to your code once you're done if you haven't been doing so along the way.

Sum of Squares

The first few squares are 1, 4, 9, 16, 25, 36, etc. So the sum of the first one of these is 1, the first two is 5, and the first three is 14. What are the next two sums of squares?

As with the previous problem, the key is to figure out how to describe the sum of the first x squares in terms of a few simpler operations and the answer to a smaller instance of the sum-of-squares problem. If you knew the value of sum of the first n-1 squares, what would you need to do to compute the sum of the first n squares? Write that down as a recurrence. Now what makes sense as a base case?

Write a recursive method public static int sps(int n). Test that it works on a range of cases. Be sure to comment your code once you're done if you haven't been doing so along the way.

Choose

The value [n choose k] represents the number of ways of picking k distinct objects out of a set of n distinct objects. For example, suppose we have four people; Alice (A), Balthazar (B), Charlize (C) and Douglass (D). How many ways could you pick a pair of them? Six: AB, AC, AD, BC, BD and CD. We've just argued that 4 choose 2 is 6. Just so you have some more test data, 5 choose 2 and 5 choose 3 are both 10 (coincidence?), and 6 choose 3 is 20. Compute 6 choose 2 on your own.

The standard definition of n choose k involves a few factorials. However, there is also a recursive definition that can be very handy. This recursive definition isn't as straight-forward as the previous two, so we'll state it here and then walk through the reasoning behind it:

This last case is the heart of the recursion -- the rest are just base cases.

Write a recursive method public static int choose(int n,int k). Test that it works on a range of cases. Be sure to comment your code once you're done if you haven't been doing so along the way.

Example Output


	    Welcome to my Amazing Recursive Calculator!

	      Please enter a non-negative integer
	    x: 6
	      Please enter a non-negative integer
	    k: 3

	    6 raised to the power of 3 is 216.
	    The sum of the first 6 squares is 91.
	    6 choose 3 is 20.
	  

Part 2 - Object Oriented Programming

In this part of the lab, we will be extending an abstract class, Lock.java, into two different kinds of locks: a combination lock,and a change lock (a combination lock where the combination changes slightly every time it is unlocked). You will also be creating an interactive program which allows user to create a lock and perform actions on it. First, save Lock.java into your lab02 directory. Lock.java contains an abstract class which you will be extending for this lab. An abstract class defines the methods which will be in any class that extends it, and can also contain working, non-abstract methods which class which extend it can use or override. Lock.java contains two variables, int combo and boolean locked, which you will use in both your locks, as well as a working method isLocked(int combo) which you should not have to change, and three methods you will have to extend in your individual lock classes.

Combination Lock


Create a file called ComboLock.java. Inside this file, you will create the ComboLock class, which will extend Lock. Your class declaration will look like
  public class ComboLock extends Lock {

In this class, you will need to create a constructor, and methods that override all of the abstract methods in Lock.java. The lock should unlock only if the user enters an integer that matches its current combination, and once unlocked, should remain unlocked until it is explicitly locked.

Change Lock


Create a file named ChangeLock.java, which contains a class ChangeLock which extends Lock.java. The methods in this class should be pretty similar to those in ComboLock except that every time the lock is unlocked, you will add one to the combination. So if the combination starts as 0001, after the first time it is unlocked it will be 0002, after the second time it is unlocked it will be 0003, and so on.

LockUI


Now we get to actually use our locks! Create a file LockUI, containing the class LockUI.java. This class should contain a main method, and is how we will let the user interact with our locks. Your user interface should do the following:
  1. Prompt the user for a combination.
  2. Ask what kind of lock they want.
  3. Create their lock. You should use the same variable to hold the lock, no matter which type it is. You can do this by declaring a variable Lock l, and then setting it equal to either a new ChangeLock, or a new ComboLock.
  4. Go into a loop and repeatidly ask them what they want to do with their lock (lock, unlock, check lock's status, reset the combination, or exit the program). You should not have to write different code depending on what kind of lock the user picked - all of the code in your loop should work for either kind, using your Lock variable.
  5. You should use a switch statement to decide what to do when they enter their choice.
  6. If at any point they enter something that isn't an integer, or isn't one of the choices, print a nice error message and let them re-enter a value.
  7. You can use System.exit(0) to exit the program when they choose to exit.
An example of what this should look like is below:
  Enter the combo for your lock.
  1234
  What kind of lock would you like to create?  Press 1 for comboLock,
  2 for changeLock
  2
  Created a new change lock
  Press 1 to unlock, 2 to lock, 3 to check the state of the lock, 4 to
  reset the combination, and 5 to exit
  1
  Please enter your combination
  1234
  Success!  Lock is open
  Press 1 to unlock, 2 to lock, 3 to check the state of the lock, 4 to
  reset the combination, and 5 to exit
  2
  Lock is locked.
  Press 1 to unlock, 2 to lock, 3 to check the state of the lock, 4 to
  reset the combination, and 5 to exit
  1
  Please enter your combination
  1234
  Failure!  Lock is still closed
  Press 1 to unlock, 2 to lock, 3 to check the state of the lock, 4 to
  reset the combination, and 5 to exit
  1
  Please enter your combination
  1234
  Failure!  Lock is still closed
  Press 1 to unlock, 2 to lock, 3 to check the state of the lock, 4 to
  reset the combination, and 5 to exit
  1
  Please enter your combination
  1235
  Success!  Lock is open
  Press 1 to unlock, 2 to lock, 3 to check the state of the lock, 4 to
  reset the combination, and 5 to exit
  3
  Unlocked
  Press 1 to unlock, 2 to lock, 3 to check the state of the lock, 4 to
  reset the combination, and 5 to exit
  4
  Please enter your new combination
  0000
  Press 1 to unlock, 2 to lock, 3 to check the state of the lock, 4 to
  reset the combination, and 5 to exit
  1
  Please enter your combination
  0000
  Success!  Lock is open
  Press 1 to unlock, 2 to lock, 3 to check the state of the lock, 4 to
  reset the combination, and 5 to exit
  5
  Goodbye!

Part 3 - Benford Analysis

Benford's Law also known as the "first-digit law" is an observation about the distribution of digits in numbers in data sources. In brief, in many natural datasets the first digit will be a '1' about 30% of the time with each higher digit occurring less frequently than the previous ending with a '9' occurring less than 5% of the time.

There is a nice graph of this distribution on the Wikipedia page: http://en.wikipedia.org/wiki/Benford's_law#Mathematical_statement

For this part of the lab, you will create a java class named Benford which finds the distribution of digits of a set of files.

Counting first digits

For this program, you are only concerned about words that start with a numerical digit, and then you need to get the value of that digit. You'll need to maintain a count of the number of words you encounter that start with the digits 0 through 9. You should do this in an array and use the digit as the index into the array.

There are a number of ways you could get at the first character, determine if it is a digit, and get the value. One way might be to:

  1. Get a word from your input (probably using Scanner and hasNext() / next()
  2. Use the word's charAt() method to get the first character and pass it into Character.isDigit() to determine if it is a digit.
  3. If it is, then you can use the word's substring method to extract that first digit as a String
  4. And that new String can be passed into Integer.parseInt to determine the integer value.

Processing files

This program will be very similar to some of the ones you have already worked on. You will give a list of files on the command line and then your program will work through all of them. If a file cannot be opened, just print a message to the user and continue on.

Printing out the frequency chart

Once you have gone through and made a count of all your leading digits, you should print out a histogram of the various frequencies that you encountered. You should should print out the following information for each row:

  1. The leading digit 0-9
  2. The total count of words beginning with that digit
  3. The frequency of that digit being at the start of words beginning with digit
  4. A bar of * characters representing the frequency graphically

The most frequently occurring digit should have a bar of length MAXWIDTH where that is defined by a constant in your class.

    public static final int MAXWIDTH = 50;

All other bar's lengths should be scaled proportionally to this. So take the digit's count, divide by the max count, and multiple by MAXWIDTH. You should round this number using Math.round() to get it to the nearest integer.

So if all the frequencies are about even, you'd expect all the bars to be about 50 units wide. If they are more varied, then the longest will be 50 and the others will be of various shorter widths. Note that if the frequency is 0, the bar should have 0 '*' characters.

To print out the table in a formatted fashion, you might want to use System.out.printf to do things. You can pass it a format string that uses %d for decimal numbers and %f for floating point numbers. You can also specify a width for each field by including a number between the % and the letter. Here is what I used in the example shown below:

    System.out.printf("%d %8d %4.1f%% : ", digit, count[digit], freq );

Sample data

I collected a number of pieces of sample data for you to try out your Benford analysis tool on: lab1-numbers.zip

Because these are large files, and I don't want you to turn them in, they are all in a subfolder called "numbers".

This data comes from the US Census Bureau, CIA World Factbook, and a variety of other sources around the web. Try running your tool on the various files. Note in your comments for your program which ones seem to follow Benford's law and which ones don't. Feel free to speculate why the ones that don't are that way.

% java Benford numbers/census-pop-est.txt
Welcome to the Benford analysis program

0     1116  0.9% : **
1    35294 29.8% : **************************************************
2    20825 17.6% : ******************************
3    14070 11.9% : ********************
4    11405  9.6% : ****************
5     9210  7.8% : *************
6     8019  6.8% : ***********
7     6809  5.7% : **********
8     6472  5.5% : *********
9     5259  4.4% : *******

% java Benford numbers/census-pop-est.txt kittens numbers/primes-10000.txt
Welcome to the Benford analysis program
Could not open kittens (No such file or directory)

0     1116  0.9% : **
1    36898 28.7% : **************************************************
2    21954 17.1% : ******************************
3    15167 11.8% : *********************
4    12474  9.7% : *****************
5    10265  8.0% : **************
6     9032  7.0% : ************
7     7836  6.1% : ***********
8     7475  5.8% : **********
9     6265  4.9% : ********

handin

Look through your programs and make sure you've included your name at the top of all of them. If you know that there is a problem in one of your programs, document that at the top. If you adhered to the honor code in this assignment, add the following statement as a comment at the top of Recnum.java, ComboLock.java, ChangeLock.java,LockUI.java, and Benford.java files:

I have adhered to the Honor Code in this assignment.

Quit Eclipse, and then go through and delete any *.class files and backup files. (If you don't quit Eclipse first, it will keep regenerating your class files when it autocompiles!)

handin

You now just need to electronically handin all your files.

    % cd          # changes to your home directory
    % cd cs151    # goes to your cs151 folder
    % handin      # starts the handin program
                            # class is 151
                            # assignment is 2
                            # file/directory is lab2

    % lshand      # should show that you've handed in something

You can also specify the options to handin from the command line

    % cd ~/cs151                 # goes to your cs151 folder
    % handin -c 151 -a 2 lab2

    % lshand      # should show that you've handed in something

Last Modified: January 30, 2019, by Cynthia Taylor.