Mastering Algorithms with Perl phần 5 doc

74 171 0
Mastering Algorithms with Perl phần 5 doc

Đang tải... (xem toàn văn)

Tài liệu hạn chế xem trước, để xem đầy đủ mời bạn chọn Tải xuống

Thông tin tài liệu

Figure 8-24. A graph and its representation in Perl Creating Graphs, Dealing with Vertices First we will define functions for creating graphs and adding and checking vertices. We put these into Graph::Base because later we'll see that our data structures are affected by whether or not a graph is directed.break package Graph::Base; use vars qw(@ISA); require Exporter; @ISA = qw(Exporter); # new # # $G = Graph->new(@V) # # Returns a new graph $G with the optional vertices @V. # sub new { my $class = shift; my $G = { }; bless $G, $class; $G->add_vertices(@_) if @_; return $G; } Page 291 # add_vertices # # $G = $G->add_vertices(@v) # # Adds the vertices to the graph $G, returns the graph. # sub add_vertices { my ($G, @v) = @_; @{ $G->{ V } }{ @v } = @v; return $G; } # add_vertex # # $G = $G->add_vertex($v) # # Adds the vertex $v to the graph $G, returns the graph. # sub add_vertex { my ($G, $v) = @_; return $G->add_vertices($v); } # vertices # # @V = $G->vertices # # In list context returns the vertices @V of the graph $G. # In scalar context (implicitly) returns the number of the vertices. # sub vertices { my $G = shift; my @V = exists $G->{ V } ? values %{ $G->{ V } } : (); return @V; } # has_vertex # # $b = $G->has_vertex($v) # # Returns true if the vertex $v exists in # the graph $G and false if it doesn't. # sub has_vertex { my ($G, $v) = @_; return exists $G->{ V }->{ $v }; } Testing for and Adding Edges Next we'll see how to check for edges' existence and how to create edges and paths. Before we tackle edges, we must talk about how we treat directedness in our data structures and code. We will have a single flag per graph (D) that tellscontinue Page 292 whether it is of the directed or undirected kind. In addition to querying directedness, we will also allow for changing it dynamically. This requires re-blessing the graph and rebuilding the set of edges. # directed # # $b = $G->directed($d) # # Set the directedness of the graph $G to $d or return the # current directedness. Directedness defaults to true. # sub directed { my ($G, $d) = @_; if (defined $d) { if ($d) { my $o = $G->{ D }; # Old directedness. $G->{ D } = $d; if (not $o) { my @E = $G->edges; while (my ($u, $v) = splice(@E, 0, 2)) { $G->add_edge($v, $u); } } return bless $G, 'Graph::Directed'; # Re-bless. } else { return $G->undirected(not $d); } } return $G->{ D }; } And similarly (though with reversed logic) for undirected. Also, the handling of edges needs to be changed: if we convert a directed graph into an undirected graph, we need to keep only either of the edges u – v and v – u, not both. Now we are ready to add edges (and by extension, paths):break # add_edge # # $G = $G->add_edge($u, $v) # # Adds the edge defined by the vertices $u, $v, to the graph $G. # Also implicitly adds the vertices. Returns the graph. # sub add_edge { my ($G, $u, $v) = @_; $G->add_vertex($u); Page 293 $G->add_vertex($v); push @{ $G->{ Succ }->{ $u }->{ $v } }, $v; push @{ $G->{ Pred }->{ $v }->{ $u } }, $u; return $G; } # add_edges # # $G = $G->add_edges($u1, $v1, $u2, $v2, . . .) # # Adds the edge defined by the vertices $u1, $v1, . . ., # to the graph $G. Also implicitly adds the vertices. # Returns the graph. # sub add_edges { my $G = shift; while (my ($u, $v) = splice(@_, 0, 2)) { $G->add_edge($u, $v); } return $G; } # add_path # # $G->add_path($u, $v, . . .) # # Adds the path defined by the vertices $u, $v, . . ., # to the graph $G. Also implicitly adds the vertices. # Returns the graph. # sub add_path { my $G = shift; my $u = shift; while (my $v = shift) { $G->add_edge($u, $v); $u = $v; } return $G; } Returning Edges Returning edges (or the number of them) isn't quite as simple as it was for vertices: We don't store the edges as separate entities, and directedness confuses things as well. We need to take a closer look at the classes Graph::Directed and Graph::undirected—how do they define edges, really? The difference in our implementation is that an undirected graph will "fake" half of its edges: it will believe it has an edge going from vertex v to vertex u, even if there is an edge going only in the opposite direction. To implement this illusion, we will define an internal method called _edges differently for directed and undirected edges.break Page 294 Now we are ready to return edges—and the vertices at the other end of those edges: the successor, predecessor, and neighbor vertices. We will also use a couple of helper methods because of directedness issues _successors and _predecessors (directed graphs are a bit tricky here). # _successors # # @s = $G->_successors($v) # # (INTERNAL USE ONLY, use only on directed graphs) # Returns the successor vertices @s of the vertex # in the graph $G. # sub _successors { my ($G, $v) = @_; my @s = defined $G->{ Succ }->{ $v } ? map { @{ $G->{ Succ }->{ $v }->{ $_ } } } sort keys %{ $G->{ Succ }->{ $v } } : ( ); return @s; } # _predecessors # # @p = $G->_predecessors($v) # # (INTERNAL USE ONLY, use only on directed graphs) # Returns the predecessor vertices @p of the vertex $v # in the graph $G. # sub _predecessors { my ($G, $v) = @_; my @p = defined $G->{ Pred }->{ $v } ? map { @{ $G->{ pred }->{ $v }->{ $_ } } } sort keys %{ $G->{ Pred }->{ $v } } : ( ); return @p; } Using _successors and _predecessors to define successors, predecessor and neighbors is easy. To keep both sides of the Atlantic happy we also definebreak use vars '*neighbours'; *neighbours = \&neighbors; # Make neighbours() to equal neighbors(). Page 295 Now we can finally return edges:break package Graph::Directed; # _edges # # @e = $G->_edges($u, $v) # # (INTERNAL USE ONLY) # Both vertices undefined: # returns all the edges of the graph. # Both vertices defined: # returns all the edges between the vertices. # Only 1st vertex defined: # returns all the edges leading out of the vertex. # Only 2nd vertex defined: # returns all the edges leading into the vertex. # Edges @e are returned as ($start_vertex, $end_vertex) pairs. # sub _edges { my ($G, $u, $v} = @_; my @e; if (defined $u and defined $v) { @e = ($u, $v) if exists $G->{ Succ }->{ $u }->{ $v }; # For Graph::Undirected this would be: # if (exists $G->{ Succ }->{ $u }->{ $v }) { # @e = ($u, $v) # if not $E->{ $u }->{ $v } and # not $E->{ $v }->{ $u }, # $E->{ $u }->{ $v } = $E->{ $v }->{ $u } = 1; # } } elsif (defined $u) { foreach $v ($G->successors($u)) { push @e, $G->_edges($u, $v); } } elsif (defined $v) { # not defined $u and defined $v foreach $u ($G->predecessors($v)) { push @e, $G->_edges($u, $v); } } else { # not defined $u and not defined $v foreach $u ($G->vertices) { push @e, $G->_edges($u); } } return @e; } package Graph::Base; Page 296 # edges # # @e = $G->edges($u, $v) # # Returns the edges between the vertices $u and $v, or if $v # is undefined, the edges leading into or out of the vertex $u, # or if $u is undefined, returns all the edges of the graph $G. # In list context, returns the edges as a list of # $start_vertex, $end_vertex pairs; in scalar context, # returns the number of the edges. # sub edges { my ($G, $u, $v) = @_; return () if defined $v and not $G->has_vertex($v); my @e = defined $u ? ( defined $v ? $G->_edges($u, $v) : ($G->in_edges($u), $G->out_edges($u)) ) : $G->_edges; return wantarray ? @e : @e / 2; } The in_edges and out_edges are trivially implementable using _edges. Density, Degrees, and Vertex Classes Now that we know how to return (the number of) vertices and edges, implementing density is easy. We will first define a helper method, density_limits, that computes all the necessary limits for a graph: the actual functions can simply use that data.break # density_limits # # ($sparse, $dense, $complete) = $G->density_limits # # Returns the density limits for the number of edges # in the graph $G. Note that reaching $complete edges # does not really guarantee completeness because we # can have multigraphs. # sub density_limits { my $G = shift; my $V = $G->vertices; my $M = $V * ($V 1); $M = $M / 2 if $G->undirected; return ($M/4, 3*$M/4, $M); } Page 297 With this helper function. we can define methods like the following: # density # # $d = $G->density # # Returns the density $d of the graph $G. # sub density { my $G = shift; my ($sparse, $dense, $complete) = $G->density_limits; return $complete ? $G->edges / $complete : 0; } and analogously, is_sparse and is_dense. Because we now know how to count edges per vertex, we can compute the various degrees: in_degree, out_degree, degree, and average_degree. Because we can find out the degrees of each vertex, we can classify them as follows: # is_source_vertex # # $b = $G->is_source_vertex($v) # # Returns true if the vertex $v is a source vertex of the graph $G. # sub is_source_vertex { my ($G, $v) = @_; $G->in_degree($v) == 0 && $G->out_degree($v) > 0; } Using the vertex classification functions we could construct methods that return all the vertices of particular type: # source_vertices # # @s = $G->source_vertices # # Returns the source vertices @s of the graph $G. # sub source_vertices { my $G = shift; return grep { $G->is_source_vertex($_) } $G->vertices; } Deleting Edges and Vertices Now we are ready to delete graph edges and vertices, with delete_edge, delete_edges, and delete_vertex. As we mentioned earlier, deleting vertices is actually harder because it may require deleting some edges first (a "dangling" edge attached to fewer than two vertices is not well defined).break Page 298 # delete_edge # # $G = $G->delete_edge($u, $v) # # Deletes an edge defined by the vertices $u, $v from the graph $G. # Note that the edge need not actually exist. # Returns the graph. # sub delete_edge { my ($G, $u, $v) = @_; pop @{ $G->{ Succ }->{ $u }->{ $v } }; pop @{ $G->{ Pred }->{ $v }->{ $u } }; delete $G->{ Succ }->{ $u }->{ $v } unless @{ $G->{ Succ }->{ $u }->{ $v } }; delete $G->{ Pred }->{ $v }->{ $u } unless @{ $G->{ Pred }->{ $v }->{ $u } }; delete $G->{ Succ }->{ $u } unless keys %{ $G->{ Succ }->{ $u } }; delete $G->{ Pred }->{ $v } unless keys %{ $G->{ Pred }->{ $v } }; return $G; } # delete_edges # # $G = $G->delete_edges($ul, $vl, $u2, $v2, ) # # Deletes edges defined by the vertices $ul, $vl, . . ., # from the graph $G. # Note that the edges need not actually exist. # Returns the graph. # sub delete_edges { my $G = shift; while (my ($u, $v) = splice(@_, 0, 2)) { if (defined $v) { $G->delete_edge($u, $v); } else { my @e = $G->edges($u); while (($u, $v) = splice(@e, 0, 2)) { $G->delete_edge($u, $v); } } } return $G; } Page 299 # delete_vertex # # $G = $G->delete_vertex($v) # # Deletes the vertex $v and all its edges from the graph $G. # Note that the vertex need not actually exist. # Returns the graph. # sub delete_vertex { my ($G, $v) = @_; $G->delete_edges($v); delete $G->{ V }->{ $v }; return $G; } Graph Attributes Representing the graph attributes requires one more anonymous hash to our graph object, named unsurprisingly A. Inside this anonymous hash will be stored the attributes for the graph itself, graph vertices, and graph edges. Our implementation can set, get, and test for attributes, with set_attribute, get_attribute, and has_attribute, respectively. For example, to set the attribute color of the vertex x to red and to get the attribute distance of the edge from P to q: $G->set_attribute('color', 'x', 'red'); $distance = $G->get_attribute('distance', 'p', 'q'); Displaying Graphs We can display our graphs using a simple text-based format. Edges (and unconnected vertices) are listed separated with with commas. A directed edge is a dash, and an undirected edge is a double-dash. (Actually, it's an ''equals" sign.) We will implement this using the operator overloading of Perl—and the fact that conversion into a string is an operator ("") in Perl Anything we print() is first converted into a string or stringified. [...]... all the vertices You can read more about depth-first and breadth-first in Chapter 5, Searching In principle, one can walk the edges in any order Because of this ambiguity, there are numerous orderings: O ( | E | !) possibilities, which grows extremely quickly In many algorithms one can pick any edge to follow, but in some algorithms it does matter in which order the adjacent vertices are traversed Whatever... know of are in Perl, sadly enough) You can try out the following packages to see whether they work for you: daVinci A graph editor from University of Bremen, http://www.informatik.uni-bremen.de/~davinci/ grapbuiz A graph description and drawing language, dot, and GUI frontends for that language, from AT&T Research, http://www research att com/sw/tools/graphviz/ Graph Traversal All graph algorithms depend... adjacent vertices are traversed Whatever we do, we must look out for cycles A cycle is a sequence of edges that leads us to somewhere where we have been before (see Figure 8- 25) Depending on the algorithm, cycles can cause us to finish without discovering all edges and vertices, or to keep going around until somebody kills the program When you are "Net surfin'," you are traversing the World Wide Web You... contain criteria such as the following: • In which order the potential root vertices are visited • Which are the potential root vertices to begin with • In which order the successor vertices of a vertex are visited • Which are the potential successor vertices to begin with An example of a useful callback for graph G would be "add this edge to another graph" for the third event, "when an edge is seen for the... not need to restart because of unreached components, but if we do need to restart, it's O ( | V | + | E | ) BFS is iterative (unlike DFS, which is recursive) In pseudocode it looks like: breadth-first ( graph G, vertex u ) create a queue with u as the initial vertex mark u as seen while there are vertices in the queue do dequeue vertex v mark v as seen enqueue unseen neighboring vertices of v done It's... individual trees, cycles, or even just individual vertices An example of a forest is the directory model of MS-DOS or VMS: they have several roots, such as the familiar A: and C: drives See Figure 8- 35 Figure 8- 35 An MS-DOS filesystem tree If every branch of a tree (including the root vertex) has no more than two children, we have a binary tree Three children make a ternary tree, and so on In the World Wide... tree as a family tree, with parent vertices and child vertices, ancestors and descendants: for example, see Figure 8-38 Family trees consist of several interlacing trees The immediate ancestors (directly connected) are predecessor vertices and the immediate descendants are successor vertices The directly connected vertices of a vertex are also called the neighbor vertices Sometimes (with adjacency lists,... mode again Topological Sort Topological sort is a listing of the vertices of a graph in such an order that all the ordering relations are respected Topology is a branch of mathematics that is concerned with properties of point sets that are unaffected by elastic transformations.* Here, the preserved properties are the ordering relations More precisely: topological sort of a directed acyclic graph (a... vertices in the order they are finished (that is, are seen for the last time,continue * A topologist cannot tell the difference between a coffee mug and a donut because they both have one hole Page 3 05 Figure 8-27 A graph and some of its topological sorts Figure 8-28 A cyclic graph cannot be sorted topologically Figure 8-29 The DAG of our garage cleaning project meaning that they have no unseen edges)... sort is Θ ( | V | + | E | ) Because web pages form cycles, topologically sorting them is impossible (Ordering web pages is anathema to hypertext anyway.) Here is the code for cleaning up the garage using Perl: break use Graph; my $garage = Graph->new; $garage->add_path( qw( move_car move_LPs move_sofa Page 306 hoover_floor wash_floor ) ); $garage->add_edge( qw( junk_newspapers move_sofa ) ); $garage->add_path( . Figure 8-24. A graph and its representation in Perl Creating Graphs, Dealing with Vertices First we will define functions for creating graphs and adding and checking. graphs using a simple text-based format. Edges (and unconnected vertices) are listed separated with with commas. A directed edge is a dash, and an undirected edge is a double-dash. (Actually, it's. We will implement this using the operator overloading of Perl and the fact that conversion into a string is an operator ("") in Perl Anything we print() is first converted into a string

Ngày đăng: 12/08/2014, 21:20

Tài liệu cùng người dùng

Tài liệu liên quan