Introduction |
This programming assignment is designed to ensure that you know how to use
combinations of generic collection classes (aka data types) to model and
compactly write code that solves a wide variety of different programming
problems.
The kind of abstract thinking that goes into modeling solutions to these
programming problems with collection classes is critical to your development
as computer scientists.
For this reason, collection classes are important and useful themselves, and
we will use our upcoming advanced knowledge of data structures to improve
the performance of programs that are written to use these collection
classes (the implementations I supply are all correct -to my knowledge- but
are slow).
You will also get some experience reading files and scanning the lines you
read.
There are five parts to this assignment; you always need to write a main method in each; sometimes you will be asked to write static helper methods; you may write any other helper methods if you find any useful. But, you may not change any of the collection classes that I supply. Please note that the Collections class (notice it is plural, there also is a much different Collection interface) contains various static methods to process collection classes: most import for this assignment is the sort method. Each part of this assignment will have its own download: a project file with with the begining of a .java file and related data files in it. These files included the imports that I used in my solution; you may import other interfaces and classes as well if you find any useful). You will submit one .java file that solves each problem. As a warmup, you should download a project that solves the Voting Problem. The code it contains is similar to the code that you will write for this assignment. You should study this code carefully and understand how it works; then, leverage off this code when you write solutions to the five problems below, especially related to reading files from data. Also, the class index contains a link to the "Javadoc of Collections API" which discusses each of the public constructors and method. IMPORTANT: When we start writing our own collection classes you will need to use Build Path to add the collections.jar file to each project that you work on NOT add it to the library. Although either method will work for this assignment (which just uses the already written implementations), I suggest you ensure you know how to use Build Path to get the job done. Again, for you home machine I suggest that you put this file into the workspace and access it from there. You should work on this assignment in pairs, with someone in your lab section. Try to find someone who lives near you, with a similar work schedule: e.g., talk about whether you like to work morning, nights, weekends. If you cannot work with someone because of some special reason(s), you should send me email stating them. Only one student should submit the assignment, but both student's names should appear in the comments at the top of each submitted .java file. Please turn in each program as you finish it, so that I can accurately assess the progress of the class as a whole during this assignment. Print this document and carefully read it, marking any parts that contain important detailed information that you find (for review before you turn in the files). Note especially, the last two sections, on Extra Credit and Time Management. These important sections are relevant to all programming assignments, but appear only in this one. The allocation of the 60 points this entire programming assignment is worth will be #1 is worth 15 pts, #2 is worth 15 pts, #3 is worth 15 pts, #4 is worth 10 pts, #5 is worth 5 pts. Note that you cannot get extra credit points for submitting this assignment early unless you have correctly solved and submitted at least problems 1-4. |
Problem #1 |
Download the Reverse project folder.
The main method in my solution contains 19 non-blank lines of code.
the printGraph and reverse methods contains 7 and 15
non-blank lines respectively.
Note that there are multiple data files for this program; test them all.
Draw the graph for each and ensure that your code correctly prints it
and reverses it.
Problem Statement (Including Input and Output)First, read a file of pairs of node names representing edges in a directed graph, building a Map whose key is the label of the source node and whose value is a Set of the destination nodes reachable from that source node. We write this informally as Map[String] -> Set[String]
Two nodes appear on each line: first the source node, then the destination
node, with these words separated by one semicolon character.
For example, the input file graph1.txt contains the following
lines (which could appear in any order):
Second, write a method named printGraph that prints all these edges,
one source node per line (sorted alphabetically) followed by the set of
all the destination nodes that it can immediately reach.
The graph above would print as
Note that the source nodes are SORTED alphabetically, but
the set of desintation nodes does NOT have to be SORTED:
you can print the Set using a toString method
(explicitly or implicitly).
Also, because node g is not a source node (it is only a
destination node), it does not appear first on any line.
Third, write a method named reverse that takes one graph as an argument,
and returns a result that is a new graph that contains all edges from the
argument graph reversed: e.g., if the first graph has an edge from source
ato detination b (a->b) then the second one has an
edge from source b to destination a (b->a).
Call printGraph to pint all these edges: again, one source node per
line (and all its edges), sorted alphabetically by source node, with no
sorting for the destination nodes.
My main method reads the graph, and then the last two lines in my code are
The reverse graph would be written as
Because node a is not a source node in the reversed graph,
it does not appear first on any line.
|
Problem #2 |
Download the Reachability project folder.
The main method in my solution contains 19 non-blank lines of code
(mostly to read in the graph);
the reachable method contains 15 non-blank lines; the
printGraph method (7 non-blank lines) comes from the solution to the
previous problem.
Note that there are multiple data files for this program; test them all.
Draw the graph for each and ensure that your code correctly prints it
and computes the nodes reachable from any source node.
These are the same files as for Problem #1: if you drew out the graphs
there, use them for this problem; if you didn't, draw them out now.
Problem Statement (Including Input and Output)First, repeat what you did in Problem #2: read a file of pairs of node names representing edges in a directed graph, building a Map whose key is the label of the source node and whose value is a Set of the destination nodes reachable from that source node. We write this informally as Map[String] -> Set[String]
Two nodes appear on each line: first the source node, then the destination
node, with these words separated by one semicolon character.
For example, the input file graph1.txt contains the following
lines:
Second, call the printGraph method to prints all these edges, one
source node per line (sorted alphabetically) followed by the Set of
all the destination nodes that it can immediately reach.
The graph above would print as
Note that the source nodes are SORTED alphabetically, but the set of desintation nodes does NOT have to be SORTED: you can print the Set using a toString method (explicitly or implicitly). Also, because node g is not a source node (it is only a destination node), it does not appear first on any line. Third, write a method named reachable that takes two parameters: the first is a graph and the second is one label of a node in that graph; this method returns a Set of that label and all the labels of nodes reachable from that one by following the edges/arrows any number of times. Here is the basic algorithm for reachability, which is simple to explain and not (very) complicated to implement by using collection classes. You should try to hand simulate it, at a high-level, assuming the Set and Queue collections work correctly, independent of how they are implemented. Use this method instead of recursion: unless recursion is done very carefully, it will run forever on graphs with cycles: one of the input files is a graph with cycles.
My main method reads the graph, and then the last two lines in my code are just
Sample InteractionThe program, as specified, will have the following interaction: user-typed information appears in italics. Your output should "match" this one. You should also check that it works for other starting nodes, and a variety of starting nodes in the other graphs.Enter name of file with graph: graph1.txt Graph: source -> {destination} edges a -> ArraySet[2/2:[0]=c,[1]=b] b -> ArraySet[1/1:[0]=d] c -> ArraySet[2/2:[0]=f,[1]=e] d -> ArraySet[1/1:[0]=g] e -> ArraySet[1/1:[0]=d] f -> ArraySet[2/2:[0]=g,[1]=d] |
Problem #3 |
Download the Finite Automaton project folder.
The main method in my solution contains 35 non-blank lines of code.
Your program will simulate a machine called a "finite automaton" (FA); sometimes this is also called a Deterministic Finite Automaton (DFA). An FA is described by its states and its transitions: each transition specifies an input and what state that input leads to. You will read the description of some arbitrary FA first; then you will simulate that FA starting in a specific state and running on a specific input.
Problem Statement (Including Input and Output)First, process a file that describes a FA. Create a Map whose keys are states and whose values are another Map of the transitions from that state: whose keys are inputs and whose values are states. Each line of the file contains a state and a description of its transitions. The first token is the state (a String) and the remaining tokens (always coming in pairs) are inputs and states. Each transition Map for a state has a key that is an input (a String) and a value that is a state (a String). All tokens are separated by one semicolon character. We write this informally as Map[String] -> (Map[String] -> String).
For example, the file parity.txt contains the following lines:
Here, the state even (meaning it has seen an even number of 1 inputs so far) is a key in the main Map. It's value is a Map with two key/value pairs 0/even and 1/odd. It means that in the even state, if the input is a 0 the FA stays in the even state; if the input is a 1 the FA goes to the odd state. And similarly (the next line) means that in the odd state, if the input is a 0 the FA stays in the odd state; if the input is a 1 the FA goes back to the even state. Second print a sorted version of the main Map: the states (for any FA, no matter how many states it has) must be printed in alphabetical order, one state per line, each followed by its associated Map of transitions. The information in the transition Maps does not have to be in alphabetical order: you can print the Map using a toString method (explicitly or implicitly).
For example, the file above would produce:
Third, process a one-line file that contains an initial state followed by a sequence of inputs. The initial state will be a state in the FA (are in the outer map) and all inputs specify legal transitions (are in the inner maps).
For example, the input file inputparity.txt contains the following
line:
Print the initial state, and then each input and the new state it transitions
to, and finally print the final state.
For the parity FA and this file, it prints
Sample InteractionThe program, as specified, will have the following interaction: user-typed information appears in italics. Your output should "match" this one.Enter Finite Automaton Description File: parity.txt Finite Automaton even transitions = ArrayMap[2/2:[0]=0->even,[1]=1->odd] odd transitions = ArrayMap[2/2:[0]=0->odd,[1]=1->even] Enter start state/inputs file: inputparity.txt Initial state = even input = 1; new state = odd input = 0; new state = odd input = 1; new state = even input = 1; new state = odd input = 0; new state = odd input = 1; new state = even Final state = even |
Problem #4 |
Download the Non-Determinisitic Finite Automaton
project folder.
The main method in my solution contains 46 non-blank lines of code.
Generally, you can update a clean solution to Problem #3 to get a clean
solution to this problem, making key changes related to the differences
created by non-determinism.
Your program will simulate a machine called a "non-deterministic finite automaton" (NDFA); contrast this with the Deterministic Finite Automaton in Problem #3. A NDFA is described by its states and its transitions: each transition specifies an input and what state (or states: that what makes it non-deterministic) that input leads to. You will read the description of some arbitrary NDFA first; then you will simulate that NDFA starting in a specific state and running on a specific input.
Problem Statement (Including Input and Output)First, process a file that describes a NDFA. Create a Map whose keys are states and whose values are another Map of the transitions from that state: whose keys are inputs and whose values are a Set of states. Each line of the file contains a state and a description of its transitions. The first token is the state (a String) and the remaining tokens (always coming in pairs) are inputs and states. Each transition Map for a state has a key that is an input (a String) and a value that is a Set of states (each state is a String). All tokens are separated by one semicolon character. We write this informally as Map[String] -> (Map[String] -> Set[String]). Compare this to the previous description (for a deterministic finite automata: Map[String] -> (Map[String] -> String)).
For example, the file endin01.txt contains the following lines:
Here, the state start is a key in the main Map. It's value is a Map with two key/value pairs 0 mapping to the Set containing start and near and 1 mapping to the the Set containing just start. It means that in the start state, if the input is a 0 the NDFA can stay in the start state or it can go to the near state; if the input is a 1 the NSFA must stay in the start state. And similarly (the next line means) that in the near state, if the input is a 1 the NDFA must go into the end state. The last line means that the end state has no transitions. Second print a sorted version of the main Map: the states (for any NDFA, no matter how many states it has) must be printed in alphabetical order, one state per line, each followed by its associated Map of transitions. The information in the transition Maps does not have to be in alphabetical order: print them the simplest way you can in Java, since the order is not important. Note that the state end is a key in the main Map, whose associated transitions are an empty Map
For example, the file above would produce:
Third, process a one-line file that contains an initial state followed by a sequence of inputs. The initial state will be in the NDFA (in the outer map) and all inputs specify legal transitions (in the inner maps).
For example, the input file inputendin01.txt contains the following
line:
Print the initial state, and then each input and the new state(s) it
transitions to, and finally print the final state.
For the endin01 NDFA and this file, it prints
Note especially that in the start state, if the input is a 0, then the NDFA can either remain in the start state or go into the near state. For this program, we keep tracek of all states that the NDFA can be in, using a Set of new possible states. For the next input, 1, we can be either in the start state (from the start state, an input of 1 allows us to stay in the start state) or the end state (from the near state, an input of 1 allows us to transition to the end state). Thus, we keep track of the Set of states the NDFA can be in, and the new Set of states the NDFA can be in after processing the next input. Sample InteractionThe program, as specified, will have the following interaction: user-typed information appears in italics. Your output should "match" this one.Enter Non-Deterministic Finite Automaton Description File: endin01.txt Non-Deterministic Finite Automaton end transitions = ArrayMap[0/1:] near transitions = ArrayMap[1/1:[0]=1->ArraySet[1/1:[0]=end]] start transitions = ArrayMap[2/2:[0]=0->ArraySet[2/2:[0]=start,[1]=near],[1]=1->ArraySet[1/1:[0]=start]] Enter start state/inputs file: inputendin01.txt Initial state(s) = ArraySet[1/1:[0]=start] input = 1; new possible states = ArraySet[1/1:[0]=start] input = 0; new possible states = ArraySet[2/2:[0]=start,[1]=near] input = 1; new possible states = ArraySet[2/2:[0]=start,[1]=end] input = 1; new possible states = ArraySet[1/1:[0]=start] input = 0; new possible states = ArraySet[2/2:[0]=start,[1]=near] input = 1; new possible states = ArraySet[2/2:[0]=start,[1]=end] Final possible state(s) = ArraySet[2/2:[0]=start,[1]=end] |
Problem #5 |
Download the Word Generator project folder.
The main method in my solution contains 64 non-blank lines of code
(my anonymous class implementing the Comparator interface was 9
non-blank lines, and the randomWord method is 2 non-blank lines).
Your program will "learn" the word pattern of an author (based on some "order statistic") and then generate random text following the author's word pattern. Problem Statement (Including Input and Output)First, prompt the user for the order statistic n: 1, 2, 3, etc. For initial testing, I suggest using an order statistic of 2; when you think your program is correct, try 3 or 4.Second, read a file of tokens, building a Map storing the following data: Map[List[Stringn]] -> List[String]. from a List of n words (n is the order statistic) to a List of all the words in the text that follow these words: e.g., if n were 2, the Map would contain a key for every pair of words appearing next to each other in the text, and a value that is a List of all the words following this key (no matter where the pair occurs, with NO DUPLICATES allowed in the values list). You can read the words one at a time, in a single loop (see readString in the TypedBufferReader class) or call readAllTokens and use the StringTokenizer class on them (using a " " to specify to use only a space as the token separators). To build the Map start by "prereading" n words into the list (assume that this is always possible; how might it not be?); then read the next word and put it in as a value associated with the List of pre-read words; then, drop the oldest word in the List, and add this next word to the end of the list (always keeping the list length at n); read the next word, and repeat the process for this word, continuing until there are no words to read.
For a simple example, the file input1.txt contains the following lines:
Third, print all the associations in the Map, one per line in lexical order; after printing all associations, print the size of the smallest and largest List that is a value in the Map. Each line contains n words in a List, followed by the List of words that follow them in the text. In lexical order, the keys appear in order relative to the first word in the List; for all first words that are the same, they appear in order relative to the second word in the List; for all first and second words that are the same, they appear in order relative to the thrid word in the List; etc. You can put all the entries in a List and then sort the list using an object from an anonymous class implementing the Comparator to do the job: its compare method finds the first different words in the two Lists (there will always be at least on different word) and return a result based on how these two words compare. Fill in the Comparator in some call to the Collections.sort method. Examine the code in the Voting Problem to see how something similar is accomplished there.
For example, the file above would produce:
So, as you can see, the only allowed transition from [a,d] is c; in the text above, a d appears twice, and it is followed each time by a c. The allowed transitions from [a,b] are c and a; in the text above, a b appears twice, first followed by c and then by a. Finally, prompt the user for the number of random words to generate (some number greater than the order statistic), and then prompt for the n words to start with. Build two lists (List[String]) both starting out with these words. The first will always contain the current n words (to be used as a key in the Map); the second will contain all the generated words. Generate a random next word from the Map: add it to both lists; then, drop the first word from the first list, so it is a list of size n again; repeat until you have generated the required number of words. Warning: you might have to stop prematurely if you generate the last n words in the text, if these words occur nowhere else. That is because in this case, there is no random word to generate following them! Print the list. A 15 element list, starting with the words a and d might print as Results = ArrayList[17/32:[0]=a,[1]=d,[2]=c,[3]=a,[4]=a,[5]=d,[6]=c, [7]=a,[8]=a,[9]=d,[10]=c,[11]=a,[12]=a,[13]=b,[14]=c,[15]=b,[16]=a]In the result we start with a d (specified by the user), we know only c can come next; then using d c we know that either b or a must come next; it randomly chooses a...
Sample InteractionThe program, as specified, will have the following interaction: user-typed information appears in italics. Your output should "match" this one.Enter Order Statistic: 2 Enter name of sample text file: input1.txt Allowed Transitions (sorted) Following ArrayList[2/2:[0]=a,[1]=a] -> any of ArrayList[2/2:[0]=b,[1]=d] Following ArrayList[2/2:[0]=a,[1]=b] -> any of ArrayList[2/2:[0]=c,[1]=a] Following ArrayList[2/2:[0]=a,[1]=d] -> any of ArrayList[1/1:[0]=c] Following ArrayList[2/2:[0]=b,[1]=a] -> any of ArrayList[2/2:[0]=d,[1]=a] Following ArrayList[2/2:[0]=b,[1]=c] -> any of ArrayList[1/1:[0]=b] Following ArrayList[2/2:[0]=c,[1]=a] -> any of ArrayList[1/1:[0]=a] Following ArrayList[2/2:[0]=c,[1]=b] -> any of ArrayList[1/1:[0]=a] Following ArrayList[2/2:[0]=d,[1]=c] -> any of ArrayList[2/2:[0]=b,[1]=a] Min List size = 1 Max List size = 2 Enter # of words to generate: 15 Enter prefix word[0]: a Enter prefix word[1]: d Results = ArrayList[17/32:[0]=a,[1]=d,[2]=c,[3]=a,[4]=a,[5]=d,[6]=c,[7]=a, [8]=a,[9]=d,[10]=c,[11]=a,[12]=a,[13]=b,[14]=c,[15]=b,[16]=a] You can also try reading a much larger file included in this project folder huck.txt, Mark Twain's, "The Adventures of Huckleberry Finn". On my machine, it took over 1/2 hour to create a Map with an order statistic of 3. It has over 90,000 entries in the Map; the biggest key triple had an associated value with 46 entries in it. The key was ArrayList[3/4:out,of,the] and its associated value was ArrayList[46/64:window,face,woods,fourth,front,jacket,hole,canoe,middle, ferryboat's,cottonwood,captain's,river,fog,range,presbyterian,tree,nest, wagon-troughs,reach,store,way,wigwam,ark,room,corner,grave,nonesuch,trouble, kitchen,old,first,hardest,nigger-patch,sugar-bowl,window-hole,brass,spoon, house,tooleries,bag,office,post-office,cabin,path,chains] When we re-implement Map with a HashT able (instead of an Array), the time could go down to just a few seconds (almost 2,000 times faster). You can use this same program to read/generate music or DNA sequences or any other data made of symbols. |
Extra Credit |
Programming assignments must be turned in on time: you can get partial credit
for a partially completed assignment, but it must be turned in on time; I
will accept no late homework unless you have an official excuse
(and it is best contact me as soon as the problem arises, not after the
due date).
In fact, there is another incentive to finish not only on time, but to
finish early.
In all programming assignments, if you turn in everything at least 24 hours before it is officialy due, you will receive 2 points of extra credit. If you turn it in 48 hours (or earlier), you will receive 3 points of extra credit. (There is no more extra credit for earlier turn-ins; I recommend NOT turning it in more than 48 hours early -specifically, wait until you receive your graded previous program before turning in a new one). This is equivalent to one half of a full grade improvement on an 60 point programming assignment. In previous quarters that I have taught, almost 75% of the students completed their assignments early and received some amount of extra credit for doing so. There are two main advantages to planning on finishing early. First, if you run into a major problem, you will have extra time to solve it before the actual due date: and even experienced programmers frequently run into such problems. Yes, this means you! Second, and more importantly, if you are racing to finish before a deadline, stress levels can go through the roof, and you become less interested in learning the material (and the whole purpose of these programming assignments is to ensure that you have learned the material) and more interested in just getting it finished. If you do not learn the material, you will be at a major disadvantage for all subsequent programming assignments and tests, because of the cumulative nature of the material in this course. Therefore, plan to finish every assignment by Tuesday or Wednesday evening (most are due on Thursday evening). Programming assignments sometimes also include an extra credit section worth a few extra points points. These are designed for students who finish early and want to continue exploring programming within the context of the assignment. The points are to acknowledge, in a small way, their extra effort. This assignment has extra credit only for turning it in early. |
Time Management |
One of the hardest parts of being in college is learning how to manage your
time.
Time management is especially important in programming courses (and in the real
world, when you are working on complicated projects with hard deadlines).
The difference between good and bad time management can have a profound impact
on how much you learn in this course, how well you perform in it, and how
much effort you actually need to expend to do well.
Generally, it is best to spread out the work on a two week-long assignment. Often there are mutliple parts to the assignment, so you complete these parts in sequence. Most assignments become available two days before the Lab they are assigned on. During the Lab they are assigned on, we will overview the assignment and you can start working on one aspect of it then as well. Some students look at an assignment and think that it is best done in one sitting. If you can do so, great; but, if you plan to work this way, do the one sitting soon after it is assigned, not the night before it is due! In this way, if you are wrong about the amount of time that it will take, you will still have adequate time to complete the assignment. I know everyone has lots of constraints on their schedules, but the programming assignments should be doable in two weeks. By meeting these time goals, you will both maximize what you learn and minimize your anxiety and the time that it takes for you to do the learning. Remember that assignments must be turned in on time: you can get partial credit for a partially completed assignment, but it must be turned in on time; I will not accept any late homework unless you have an official excuse. Finally, if you find yourself falling behind, seek help immediately (from me, the TAs, or even other students in the course -when appropriate). |