David Evans cs.virginia/evans
description
Transcript of David Evans cs.virginia/evans
David Evanshttp://www.cs.virginia.edu/evans
CS201J: Engineering SoftwareUniversity of VirginiaComputer Science
Lecture 7: A Tale of Two Graphs (and a tree)
23 September 2003 CS 201J Fall 2003 2
public class Graph { // OVERVIEW: // A Graph is a mutable type that // represents an undirected // graph. It consists of nodes that are // named by Strings, and edges that // connect a pair of nodes. // A typical Graph is: // < Nodes, Edges > // where // Nodes = { n1, n2, …, nm } // and // Edges = { {from_1, to_1}, // …, {from_n, to_n} }
Graph ADT
A
B
C
D
Nodes = { A, B, C, D }Edges = { { A, B }, { A, C }, { B, C }, { A, D } }
{ … } means its a set – order doesn’t matter
23 September 2003 CS 201J Fall 2003 3
Representation Ideas
• Set of Nodes, Set of Edgese.g., Nodes = { A, B, C, D }
Edges = { <A, B>, <A, C>, <A, D>, <B, C> }
• Set of Nodes and Neighborse.g., Graph = { <A, {B, C, D}>, <B, {A, C}>,
<C, {A, B}>, <D, {A}> }
Each entry is pair of node name, and names of nodes it is connected to.
A
B
C
D
23 September 2003 CS 201J Fall 2003 4
Representation Ideas
• Set of Nodes and Matrix of booleans e.g., Nodes = [ A, B, C, D ]
Edges = [ [ 0 1 1 1 ]
[ 1 0 1 0 ]
[ 1 1 0 0 ]
[ 1 0 0 0 ] ]
A
B
C
DNo edge from A to A
Edge from B to C
23 September 2003 CS 201J Fall 2003 5
Implementation 1class Edge {
// OVERVIEW: Record type for representing an edge.
String node1, node 2;
Edge (String n1, String n2) { node1 = n1; node2 = n2; }
}
class Graph { // OVERVIEW: A Graph is a mutable type that represents an … Vector nodes; // A Vector of String objects Vector edges; // A Vector of Edge object
…}
23 September 2003 CS 201J Fall 2003 6
Rep Invariantclass Edge {
String node1, node 2;
}
class Graph { Vector nodes; // A Vector of String objects Vector edges; // A Vector of Edge object … }
RI (c) = c.nodes != null && c.edges != null
&& !c.nodes.containsNull && !c.edges.containsNull
&& elements of c.nodes are String objects
&& elements of c.edges are Edge objects
&& no duplicates in c.nodes
&& no duplicates in c.edges
&& every node mentioned in c.edges is also in c.nodes
Is this precise enough?
Function from rep to boolean
23 September 2003 CS 201J Fall 2003 7
Rep InvariantRI (c) = c.nodes != null && c.edges != null
&& !c.nodes.containsNull && !c.edges.containsNull
&& elements of c.nodes are String objects && elements of c.edges are Edge objects
&& no duplicates in c.nodes // No duplicate edges, node1/node2 are interchangable:
&& ((c.edges[i].node1 = c.edges[j].node1
&& c.edges[i].node2 = c.edges[j].node2) || (c.edges[i].node1 = c.edges[j].node2
&& c.edges[i].node2 = c.edges[j].node1)) i == j && every node mentioned in c.edges is also in c.nodes
23 September 2003 CS 201J Fall 2003 8
Abstraction Function
• Function from rep to abstract notion (use notation from overview)
AF (c) =
< Nodes, Edges >
where …
public class Graph { // OVERVIEW: // A Graph is a mutable type that // represents an undirected // graph. It consists of nodes that are // named by Strings, and edges that // connect a pair of nodes. // A typical Graph is: // < Nodes, Edges > // where // Nodes = { n1, n2, …, nm } // and // Edges = { {from_1, to_1}, // …, {from_n, to_n} }
23 September 2003 CS 201J Fall 2003 9
Abstraction Function
class Edge {
String node1, node 2;
}
class Graph { Vector nodes; // A Vector of String objects Vector edges; // A Vector of Edge object … }
Nodes = { c.nodes[i] | 0 <= i < c.nodes.size () }
The set of nodes is the elements of the c.nodes Vector
Edges = { { c.edges[i].node1, c.edges[i].node2 } | 0 <= i < c.edges.size () }
AF (c) = < Nodes, Edges > where
The set of edges is the elements of the c.edges Vector
23 September 2003 CS 201J Fall 2003 10
Implementing Constructor
public Graph () // EFFECTS: Initializes this to a graph with no nodes or
// edges: < {}, {} >.
class Edge {
String node1, node 2;
}
class Graph { Vector nodes; // A Vector of String objects Vector edges; // A Vector of Edge object … }
nodes = new Vector (); edges = new Vector ();}
How do we know this satisfies the rep invariant?
23 September 2003 CS 201J Fall 2003 11
Implementing addNode
public void addNode (String name) { // REQUIRES: name is not the name of a node in this // MODIFIES: this // EFFECTS: adds a node named name to this: // this_post = < this_pre.nodes U { name }, this_pre.edges >
class Edge {
String node1, node 2;
}
class Graph { Vector nodes; // A Vector of String objects Vector edges; // A Vector of Edge object … }
nodes.addElement (name);}
How do we know this still satisfies the rep invariant?
23 September 2003 CS 201J Fall 2003 12
Implementing addEdge
public void addEdge (String fnode, String tnode) // REQUIRES: fnode and tnode are names of nodes in this. // MODIFIES: this // EFFECTS: Adds an edge from fnode to tnode to this: // this_post = < this_pre.nodes, // this_pre.edges U { {fnode, tnode} } >
class Edge {
String node1, node 2;
}
class Graph { Vector nodes; // A Vector of String objects Vector edges; // A Vector of Edge object … }
edges.addElement (new Edge (fnode, tnode));}
Would edges.addElement (new Edge (tnode, fnode));be correct?
How do we know this still satisfies the rep invariant?
23 September 2003 CS 201J Fall 2003 13
Implementing getNeighbors
public StringSet getNeighbors (String node) // REQUIRES: node is a node in this // EFFECTS: Returns the StringSet consisting of all nodes in this // that are directly connected to node: // \result = { n | {node, n} is in this.edges
class Edge {
String node1, node 2;
}
class Graph { Vector nodes; // A Vector of String objects Vector edges; // A Vector of Edge object … }
StringSet res = new StringSet (); Enumeration edgeenum = edges.elements (); while (edgeenum.hasMoreElements ()) { Edge e = (Edge) edgeenum.nextElement (); if (e.node1.equals (node)) { res.insert (e.node2); } else if (e.node2.equals (node)) { res.insert (e.node1); }}
23 September 2003 CS 201J Fall 2003 14
Representation Ideas
• Set of Nodes, Set of Edgese.g., Nodes = { A, B, C, D }
Edges = { <A, B>, <A, C>, <A, D>, <B, C> }
• Set of Nodes and Neighborse.g., Graph = { <A, {B, C, D}>, <B, {A, C}>,
<C, {A, B}>, <D, {A}> }
Each entry is pair of node name, and names of nodes it is connected to.
A
B
C
D
23 September 2003 CS 201J Fall 2003 15
Implementation 2class NodeNeighbors {
// OVERVIEW: Record type for representing an edge.
String node;
StringSet neighbors; // A Set of String objects
NodeNeighbors (String n) { node = n; neighbors = new StringSet (); }
}
class Graph { // OVERVIEW: A Graph is a mutable type that represents an … Vector nodes; // A Vector of NodeNeighbors objects …}
23 September 2003 CS 201J Fall 2003 16
Rep Invariantclass NodeNeighbors { String node; StringSet neighbors; }class Graph { Vector nodes; // A Vector of NodeNeighbors objects}
RI (c) = c.nodes != null
&& !c.nodes.containsNull
&& elements of c.nodes are NodeNeighbors objects
&& no duplicates in c.nodes && for each node in c.nodes, each node in
c.nodes[i].neighbors is a node in c.nodesc.nodes[i].neighbors does not contain
duplicates
Function from rep to boolean
23 September 2003 CS 201J Fall 2003 17
Abstraction Function
Nodes = { c.nodes[i].node | 0 <= i < c.nodes.size () }
The set of nodes is the elements of the c.nodes Vector
Edges = { { c.nodes[i].node, c.nodes[i].neighbors[e] } | 0 <= i < c.nodes.size (),
0 <= e <= c.nodes[i].neighbors.size () }
AF (c) = < Nodes, Edges > where
class NodeNeighbors { String node; StringSet neighbors; }class Graph { Vector nodes; // A Vector of NodeNeighbors objects}
23 September 2003 CS 201J Fall 2003 18
Implementing Constructor
public Graph () // EFFECTS: Initializes this to a graph with no nodes or
// edges: < {}, {} >. nodes = new Vector ();}
class NodeNeighbors { String node; Vector neighbors; // A Vector of String objects}class Graph { Vector nodes; // A Vector of NodeNeighbors objects}
23 September 2003 CS 201J Fall 2003 19
Implementing addNode
public void addNode (String name) { // REQUIRES: name is not the name of a node in this // MODIFIES: this // EFFECTS: adds a node named name to this: // this_post = < this_pre.nodes U { name }, this_pre.edges >
nodes.addElement (new NodeNeighbors (name));}
How do we know this still satisfies the rep invariant?
class NodeNeighbors { String node; StringSet neighbors; }class Graph { Vector nodes; // A Vector of NodeNeighbors objects}
23 September 2003 CS 201J Fall 2003 20
Implementing addEdge
public void addEdge (String fnode, String tnode) // REQUIRES: fnode and tnode are names of nodes in this. // MODIFIES: this // EFFECTS: Adds an edge from fnode to tnode to this: // this_post = < this_pre.nodes, // this_pre.edges U { {fnode, tnode} } >
NodeNeighbors n1 = lookupNode (fnode); NodeNeighbors n2 = lookupNode (tnode); n1.neighbors.insert (tnode); n2.neighbors.insert (fnode);} How do we know this still satisfies
the rep invariant?
class NodeNeighbors { String node; StringSet neighbors; }class Graph { Vector nodes; // A Vector of NodeNeighbors objects}
We needto implementlookupNodealso.
23 September 2003 CS 201J Fall 2003 21
Implementing getNeighbors
public StringSet getNeighbors (String node) // REQUIRES: node is a node in this // EFFECTS: Returns the StringSet consisting of all nodes in this // that are directly connected to node: // \result = { n | {node, n} is in this.edges NodeNeighbors n = lookupNode (node);
return n.neighbors;}
class NodeNeighbors { String node; StringSet neighbors; }class Graph { Vector nodes; // A Vector of NodeNeighbors objects}
Almost…but we have exposed our rep!
23 September 2003 CS 201J Fall 2003 22
Rep ExposureWhat if client does this?
Graph g = new Graph ();
g.addNode (“A”);
g.addNode (“B”);
g.addEdge (“A”, “B”);
StringSet neighbors = g.getNeighbors (“A”);
neighbors.insert (“C”);
Does the rep invariant for g still hold?
23 September 2003 CS 201J Fall 2003 23
Rep Exposure
• If mutable components of the representation are accessible to clients, the implementation exposes the rep!
• Clients can mutate the representation directly – without using data type operations
Why is this bad?
23 September 2003 CS 201J Fall 2003 24
Problems with Rep Exposure
• Client mutations could break the rep invariant
• Client code may break if ADT implementation changes
• No longer possible to reason about the invariant being true by just checking the ADT implementation
23 September 2003 CS 201J Fall 2003 25
return n.neighbors;
Implementing getNeighbors
public StringSet getNeighbors (String node) // REQUIRES: node is a node in this // EFFECTS: Returns the StringSet consisting of all nodes in this // that are directly connected to node: // \result = { n | {node, n} is in this.edges
return n.neighbors.copy ();
class NodeNeighbors { String node; StringSet neighbors; }class Graph { Vector nodes; // A Vector of NodeNeighbors objects}
NodeNeighbors n = lookupNode (node);
} If we return a copy, the client doesn’thave access to the actual neighbors objectin the representation.
23 September 2003 CS 201J Fall 2003 26
Which implementation is better?• Depends what we care about• Code complexity
– Normally the most important criteria– Nodes/Edges: getNeighbors is harder– NodeNeighbors: toString is harder, addEdge a little
harder
• Memory Use– Nodes/Edges: 2 vectors, each edge requires 2 strings– NodeNeighbors: 1 vector, number of nodes StringSets,
each edge requires 1 string
23 September 2003 CS 201J Fall 2003 27
Which implementation is better?• Performance
– Both have poor performance: linear search through all the nodes to find one
– NodeNeighbors getNeighbors does less work– Other methods Nodes/Edges usually less
work– If we expect clients to call getNeighbors a lot,
NodeNeighbors might be better
23 September 2003 CS 201J Fall 2003 28
Performance Comparison
> time java GraphTest // Using Nodes/Edges impl1.220u 0.020s 0:01.25 99.2% > time java GraphTest // Using NodeNeighbors impl0.660u 0.040s 0:00.79 88.6%
Very rough comparison…but NodeNeighbors appearsto be twice as fast for this test case.
What is the test case doing?
23 September 2003 CS 201J Fall 2003 29
GraphTest.javapublic class GraphTest { static public void main (String args[]) { Graph g = new Graph (); int numnodes = 1000;
for (int i = 0; i < numnodes; i++) { g.addNode ("node" + i); } for (int i = 0; i < numnodes - 1; i++)
{ g.addEdge ("node" + i, "node" + (i + 1)); } for (int i = 0; i < numnodes - 2; i++)
{ g.addEdge ("node" + i, "node" + (i + 2)); } for (int i = 0; i < numnodes; i++) { StringSet neighbors = g.getNeighbors ("node" + i); } }}
23 September 2003 CS 201J Fall 2003 30
Charge
When picking representations, focus on complexity of implementation
Your time is (usually) more valuable than the
computer’s!