 #### 聯系方式 #### 您當前位置：首頁 >> Java編程Java編程

###### 日期：2019-06-08 07:36

PA 9: Application of Graph Traversal

Algorithm and Minimum Spanning Tree

Checkpoint due (+5 extra credit): Monday, midnight

Submit all the part 1 except A*

Due: June 7th, Friday 11:59 PM (midnight)

200 points

Overview

In the first part of the assignment, you will build your own navigation system just like

Google Maps using four different graph traversal algorithms we learn from class: DFS,

BFS, Dijkstra and A*.

In the second part of the assignment, you will implement a data structure Disjoint Set

(Union Find) to help you implement Kruskal’s algorithm and find a Minimum Spanning

Tree for a given Graph.

This assignment is an individual assignment. You may ask Professors/TAs/Tutors

for some guidance and help, but you can’t copy code. You may discuss the assignment

conceptually with your classmates, including bugs that you ran into and how you fixed

them. However, do not look at or copy code. This constitutes an Academic Integrity

Violation.

START EARLY!

Style Requirements

IntelliJ has a great plugin to check your style automatically. Use it!

There are style considerations beyond indentation and such.

● Choose names for test methods and for variables that are descriptive and useful

when you see them in test output.

● Consider writing helper methods if you repeatedly use the same set of

statements.

and the natural flow of code.

Part 0 - Starter code and PA quiz

Our starter code will be distributed from

https://github.com/ucsd-ets/dsc30sp19-startercode.

Note that this assignment has two parts, and you should create two separate

projects in IntelliJ to implement each part, and submit each part on gradescope

separately.

PA quiz (Due Monday)

Fill out this quiz about the PA to make sure you have understood the assignment

thoroughly. You have 5 attempts to complete it.

https://forms.gle/VSBimvhFPpUVG9SK6

Part 1 - Graph Traversal Algorithms

1.1 Intro and Analysis of Graph Traversal Algorithms

Graph traversal algorithms have many useful applications in real life. The four

algorithms we will discuss in class have various applications: DepthFirstSearch,

BreathFirstSearch, Dijkstra and A*. In this part of the assignment, you will use these

First, let’s make a guess about which algorithm could potentially be used in Google

Maps to find the shortest path between two locations. For simplicity, assume that

there exists at least one path that connects two given locations. You will probably

cross out DFS first. From what we have learned, DFS will find a path that connects two

vertices, but most likely, this path will not be the shortest. In a large graph like the Earth,

the path generated by DFS will probably spend its whole life on heading towards a

wrong direction.

What about BFS then? We know that with a finite number of vertices in an unweighted

graph, we are guaranteed to find the shortest path connecting two given vertices using

BFS. However, in an actual map, edges between two vertices are typically weighted

rather than unweighted. Roads (edges) that connect different cities (vertices) vary by

length, so we need to assign different weights to each road (edge). Furthermore, traffic

avoid traffic. Since we know that BFS will most likely fail to find the shortest path in a

weighted graph, we need to continue to the improved version of BFS: Dijkstra.

As we all know, Dijkstra can be used to find the shortest path connecting two given

vertices in a weighted graph. However, once you understand the way Dijkstra explores

the graph, you will realize that Dijkstra is not an efficient algorithm for navigation

system. In a large graph, Dijkstra takes much longer to run for two vertices that are far

away from each other, because Dijkstra will find the shortest path from starting

node to all the other reachable nodes. Although it’s guaranteed that Dijkstra can find

the shortest path, it will search “blindly” for the shortest path: the algorithm will keep

looking for our destination in every possible direction (searching useless parts of the

graph), even if we already know the direction of our destination.

(Dijkstra will explore every location within nearly two thousand miles of Denver before it

locates NYC.)

To solve this problem more efficiently, we will introduce you to an algorithm that “knows”

that we need to first explore east in the example above: A*. Given that we know the

location of our destination, A* can use a simple heuristic function to guide its search.

And with a heuristic function that satisfies certain conditions, A* can still find the shortest

path between two given nodes just like Dijkstra, but much faster. In fact, although

Google Maps is using more complex and efficient algorithms, the idea of the A*

algorithm is its basis.

1.2 Build the Graph

Before starting to implement algorithms for our navigation system, we first need to

consider a well-defined way to represent our “map” using a graph.

As you might know from class, a graph consists of two components: vertices and edges.

Therefore in order to define our “map” as a graph, it is quite natural for us to define

“locations on map” to be the vertices, and “all the roads that connects two locations” to

be the edges.

Since the goal of our navigation system is essentially finding a path that connects two

given locations on the map, we can define our problem as the following:

Given two vertices, find a sequence of edges that connects these two vertices.

Notice that with a finite number of vertices in our graph, if we apply DFS, BFS, Dijkstra

and A* to our graph to find a path (assume there exists at least one path that connects

two given vertices), we might end up with totally different results. We have provided a

wonderful GUI for you to visualize the result path. After you finish implementing each

one of the algorithms, you can use this GUI to check if the path match your expected

behavior of the algorithm. We will give more details about how to use this GUI later.

1.2.1 Vertex and Edge

One simple dataset you will use to build your graph consists of two input files: cityxy.txt

and citypair.txt.

In our graph, the vertices are defined by cityxy.txt, where each line has the name of a

city followed by its x and y coordinates. For example, a line “LasVegas, 160, 350” in

cityxy.txt means we should have a vertex with name “LasVegas”, x-coordinate “160”

and y-coordinate “350” in our graph.

Meanwhile, the edges in our graph are defined by citypair.txt, where each line has a pair

of cities followed by the distance between them, which implies the “weight” of this

undirected edge. For example, a line “LosAngeles, LasVegas, 100” in citypair.txt means

we should have an undirected edge between vertex “LosAngeles” and vertex

“LasVegas” with weight “100”.

In the starter code, we have provided the full Edge class, but only part of the Vertex

class for you. You need to think about what other instance variables and methods

you will need to add to Vertex class after you understand more details about

Graph class. More specifically, you need to decide how to represent all the adjacent

edges of a given vertex, so that it’s possible to traverse from this vertex to its adjacent

vertices. Furthermore, when implementing DFS, BFS, Dijkstra and A*, you need to think

about how you can keep track of some other important information in Vertex class to

make these algorithms work.

1.2.2 Graph

In this class, you will implement all the methods to help you find the path between two

given locations in our navigation system.

We won’t give you detailed instructions in this part, the comments in starter code should

give you an idea of the purpose of each method.

Here are some clarifications on implementation details:

1. We didn’t define any instance variables in this class. You need to think about

which data structure you can use to store all the vertices with edges so that you

can quickly access them.

2. Once you decide which data structure you want to use, check its documentation

to find a method to help you return a Collection of Vertices in getVertices()

3. Let vx

and vy

be x and y coordinates of a given vertex v . The Euclidean

distance between two vertices u, v is given by this formula

4. computeAllEuclideanDistances()will change the user-defined distance of

each edge in the graph to be the Euclidean distance between two locations on

the map.

5. Before you start exploring your graph using different algorithms, you need to

reset some of the instance variables of all the vertices using a helper method

resetAllVertices()

6. CostVertex is a completed helper class for Dijkstra and A* algorithm that you

might find handy. It wraps around a vertex with its “cost”, so that we can use the

“cost” to determine its priority in a priority queue.

7. Think about when you need to stop exploring in our four graph traversal

algorithms, so that you don’t spend time exploring the useless parts of the graph.

8. In these four graph traversal algorithms, the order for selecting children of a

vertex should be the same as the order of the edges of this vertex (Start from the

first edge to the last edge). Since the order of the edges of any vertex is defined

by the user input in citypair.txt, we avoid ambiguity here when exploring the

graph. (Note that you should be using stack to implement DFS and push the

outgoing edges of a given vertex in the same order as mentioned above)

9. Notice that none of the graph traversal algorithms return a path at the end of the

methods. Instead, getpath() is the method we will use to return a list of edges

that connects two given locations (The order of the returned edges should be the

same as the order of edges you get when traversing from start vertex to end

vertex). Later in Display.java, it’s assumed that one of our four graph

traversal algorithms such as BFS() will be called before we call getpath() to

get the results.

10.Once you are done implementing one of the algorithms like DFS (make sure you

finished getPath() and all the methods on the top first), you can proceed to the

next part to use the provided GUI to visualize the results.

Note: feel free to create other methods here (public is OK) if you think the given

methods are not enough for you, you might potentially need some getters here.

1.3 - Fun with GUI

We have provided Display.java, a GUI for visualizing the output of your graph. You

don't need to know how Display.java is implemented (it'd be good if you want to

learn about Java GUI though). Although you do need to write two small parts in

paintGraph(), check TODO in Display.java for more details there.

To run the GUI, you can simply select Run on the top menu of IntelliJ and then click

Run “Display”.

Conceptually, you can imagine Display.java doing the following:

1. Instantiating a new instance of Graph and create vertices / edges from the files

cityxy.txt and citypairs.txt. You can change the file names to play around with

different graphs using the Load / Reset button to load different input files. Notice

that the input files should be placed in “PA9” folder for the GUI to be able to find

those files. You don’t need to handle any command line arguments here, just

type the name of the files and the GUI will do the work for you.

2. Notice that the weight of the edges are defined in the input file citypairs.txt by

default. Because the weight of the edge is not necessarily the distance between

two vertices, you can define the weight of any edges in your own graph.

3. If you only want the weight of your edges to be the true distance between two

vertices, you can simply click Compute All Euclidean Distances, it will call

graph.computeAllEuclideanDistances(). Then the GUI will show all the

updates on the distance variable of Edge objects in the graph. For example, let’s

say the user defined “LosAngeles,111,395”, “LasVegas,160,350” to be two

vertices, and “LosAngeles,LasVegas,100” to be the weight of the edge that

connects them. The default weight of this edge will be 100, but once you click

Compute All Euclidean Distances, our program will calculate the true distance

between LosAngeles and LasVegas: √(111 ? 160) 395 50) 6.5 . And the 2 + ( ? 3

2 ≈ 6

weight of this edge will be updated to 66.5.

4. Clicking on the blue button Draw Dijkstra's Path, it will call graph.Dijkstra()

with the starting city and targeting city names as the parameters. Then it will

highlight the edges returned by graph.getPath() between the starting city

and the targeting city. Same idea for the other 3 buttons.

Graph graph = new Graph();

String startingCity = // get text from selected value in dropdown

String targetingCity = // get text from selected value in other dropdown

graph.Dijkstra(startingCity, targetingCity);

List<Edge> path = graph.getPath(startingCity, targetingCity);

1.4 More Input files and Testing

Right now we only have 2 set of input files: cityxy.txt and citypairs.txt and smallcityxy.txt

and smallcitypairs.txt. Notice that you can customize your input files (need to be in the

right format) and play with the new resulting GUI. You can potentially use your own

input files to create a maze here for your algorithms to solve!

Check this out if you want to further visualize how each algorithm explore the graph

:https://qiao.github.io/PathFinding.js/visual/

Be aware that matching all the outputs below doesn't necessarily mean that you will

pass the autograder test. The test will compare the outputs of your getPath() against

the outputs of our solution code. You need to make sure you put all the edge in the right

sequence (The sequence of edges starting from the start vertex to end vertex).

Test case 1 (input files cityxy.txt and citypairs.txt)

After clicking Compute All Euclidean Distances:

From Los Angeles to Atlanta, using DFS

From Los Angeles to Atlanta, using BFS

From Los Angeles to Atlanta, using Dijkstra / A* ( hvalue: Euclidean distance )

Test case 2 (input files cityxy.txt and citypairs.txt)

After clicking Compute All Euclidean Distances:

From Vancouver to Miami, using DFS

From Vancouver to Miami, using BFS

From Vancouver to Miami, using Dijkstra / A* ( hvalue: Euclidean distance )

Test case 3 (input files smallcityxy.txt and smallcitypairs.txt)

After clicking Compute All Euclidean Distances:

From SanFrancisco to NewYork, using DFS

From SanFrancisco to NewYork, using BFS

From SanFrancisco to NewYork, using Dijkstra / A* ( hvalue: Euclidean distance )

Test case 4 (input files ucsdxy.txt and ucsdpairs.txt)

After clicking Compute All Euclidean Distances:

From Warren to Revelle, using DFS:

From Warren to Revelle, using BFS:

From Warren to Revelle, using Dijkstra / A* ( hvalue: Euclidean distance )

Test case 5 for A* only (input files cityxy.txt and citypairs.txt)

Change your hvalue method to make hvalue to be 4 times Euclidean distance here.

The large factor here will make A* become too greedy (make it behave more like best

first search, quickly approach the goal but the path is not the shortest).

From Vancouver to Miami, using A* ( hvalue: 4 * Euclidean distance )

You should get the results below:

Part 2 - Disjoint Set and Minimum Spanning Tree

2.1 Graph Class

You will start this part of the assignment by first implementing most of the methods in

Graph class (except for runKruskalsAlg()).

This part should be quite similar to how you implement the Graph class in part 1.

However, there are some slightly different details here.

1. Notice that now when we add a new undirected edge to our graph, we also need

to add this new edge to allUndirectedEdges, which is an ArrayList of Edge

in this class to store all the undirected edges. As allUndirectedEdges will be

used in Kruskal’s Algorithm, you must use the following rules to make your

Kruskal’s Algorithm return the correct set of edges: when adding a new

edge with nameU as source vertex, nameV as target vertex and given

weight as weight to allUndirectedEdges.

2. Another main difference here is that our constructor for Graph has a boolean

value edgesGiven, which indicates if the user will provide self-defined edges in

the graph. If edgesGiven is given as true, the method populateAllEdges()

should be disabled. Otherwise if edgesGiven is given as false, addEdge(),

disabled. (To disable any method here, check the boolean and if disabling is

necessary, print any error message then directly return at first)

Note that the purpose of this boolean and disabling appropriate methods is to

accommodate our GUI later. In our GUI, the users are given two options to

compute the minimum spanning tree of the graph defined by input files.

The first option is computing the minimum spanning tree using edges defined

by the input edges file. In this case, edgesGiven will be true.

The second option is computing the minimum spanning tree using all the

possible edges for the given vertices without considering the edges input

file. In this case, edgesGiven will be false. (More on this later)

It is easy to see that if the user wants to use the first option, which is computing

the minimum spanning tree using edges defined by the input edges file, then a

edges that is not defined by input edges file.

3. In populateAllEdges(), you should add all the possible n(n ? 1) / 2 undirected

edges with weight as Euclidean Distance to allUndirectedEdges in a

graph with n vertices. (You don’t need to add these edges to the adjacent list of

any vertex here.)

You need to think about how to add exactly n(n ? 1) / 2 undirected edges without

adding any duplicate undirected edges. (i.e if undirected edge from u to v was

added, then undirected edge from v to u should not be added)

Hint: Consider using the following code to convert your vertices collection to an

Collection<Vertex> vCollection = getVertices();

Vertex[] vertices = vCollection.toArray(new Vertex[vCollection.size()]);

2.2 Disjoint Set Implementation

In this class, you will implement Disjoint Set, which will be useful in Kruskal’s algorithm

to determine if adding a new edge will create a cycle in the graph. Disjoint set has a

simple and beautiful implementation and can check if two vertices are connected in

nearly constant runtime.

You probably need to add some additional instance variables to Vertex class to make

class for more details.

public Vertex find(Vertex v)

Returns the “sentinel node” representing the set that this vertex belongs to. You must

apply path compression here to optimize the runtime.

public void union(Vertex v1, Vertex v2)

Merges the two sets that two given vertices belongs to. If two given vertices belongs

to the same set, return. Otherwise, apply union by size: make the “sentinel node” of

smaller set as child of “sentinel node” of larger set.

(The union operation here can be done using either union by height or union by size.

The choice here is up to you, but we suggest using union by size here as it is slightly

easier to implement. )

2.3 Testing Disjoint Set

Now you should create a tester named DisjointSetTester.java to verify the

correctness of your disjoint set data structure.

Make sure you have tested the following cases:

1. After you union a set of vertices, calling find on each of them should return the same

sentinel node.

2. For two vertices that are not in the same set, calling find should return different

sentinel node.

2.4 Implement Kruskal’s Algorithm

Now go back to your Graph class and implement the method runKruskalAlg(), now

that Disjoint Set can help you efficiently check if two vertices are in the same connected

component. Here are some notes that you need to know to implement this algorithm:

1. To save us from recomputing the same result, if the result was already

computed before, directly return the result at first.

2. Use the code below to sort allUndirectedEdges in increasing order of weight:

Collections.sort(allUndirectedEdges, Comparator.comparingDouble(e ->

e.getDistance()));

3. Assume that there are v vertices in your graph and all the vertices are in the

same connected component, notice that when your result contains v - 1 edges,

the MST of this graph should already be defined by these edges. The reason is

knowing that the graph is connected and there is no cycle in the MST (since MST

is a tree) implies that there are v - 1 edges in the MST. Using this property, think

about how you can slightly improve the runtime of Kruskal’s Algorithm.

After you finish implementing Kruskal’s, just as in part 1, you should check the

TODO in Display.java to implement two small parts in order to display the

graph correctly.

2.5 Testing Kruskal’s Algorithm

Test case 1 (input files cityxy.txt and citypairs.txt)

Click Load Graph With Edges -> Click Run Kruskal’s Algorithm.

You should see the following result:

Qi

Click Load Graph With Edges -> Click Edge Weight: Euclidean Distance -> Click

Run Kruskal’s Algorithm. You should see the following result:

Click Load Graph Without Edges -> Click Run Kruskal’s Algorithm. You should see

the following result:

Test case 2 (input files ucsdxy.txt and ucsdpairs.txt)

(Imagine you are working for AT&T, and you want know a internet/phone network

design that connects all the locations below on UCSD campus with minimum total cost.

Then you should use MST algorithm such as Kruskal’s to find the MST as your network

design)

Click Load Graph With Edges -> Click Edge Weight: Euclidean Distance -> Click

Run Kruskal’s Algorithm. You should see the following result:

Click Load Graph Without Edges -> Click Run Kruskal’s Algorithm. You should see

the following result:

Submission

There are two parts of submission: PA9 Part 1 and PA9 Part 2

Submission for PA9 Part 1:

Note: You can try using Manhattan distance or manipulate the factor of your hValue

before the final submission, but in the final submission, make sure you are using

Euclidean distance as the heuristic function.

Files to submit:

1. Vertex.java

2. Graph.java

3. Display.java

Submission for PA9 Part 2:

Files to submit:

1. Vertex.java

2. DisjointSet.java

3. DisjointSetTester.java

4. Graph.java

5. Display.java

After you submit your files to GradeScope, wait for the output of submission

script. Make sure you see this after your submission:

THIS IS A SUCCESSFUL SUBMISSION. YOUR SCORE WILL BE UPDATED ONCE