Beefy Boxes and Bandwidth Generously Provided by pair Networks
P is for Practical
 
PerlMonks  

connected component

by water (Deacon)
on May 14, 2005 at 14:26 UTC ( [id://457074]=perlquestion: print w/replies, xml ) Need Help??

water has asked for the wisdom of the Perl Monks concerning the following question:

Could someone recommend a good module for enumerating all sets of connected components in an undirected graph? I could not determine which CPAN module would best serve. Thanks --

water

Replies are listed 'Best First'.
Re: connected component
by spurperl (Priest) on May 14, 2005 at 15:36 UTC
    The Graph:: family of classes on CPAN should be able to help you. Using Graph::Undirected you can use the connected_components() method.
Re: connected component
by mrborisguy (Hermit) on May 14, 2005 at 15:52 UTC
    alright... i looked at Graph, which has a directed mode (i assume that's what you mean by uni-directed - i'm thinking uni is one, so one direction -, i could be wrong)
    for an undirected graph, you could use $g->connected_components. i don't think it explicitly says how to get this for a directed graph. you'd have to roll your own... my idea is at least O(n^2) so there's probably a better way, but here ya go:
    sub di_graph_connected { my $g = shift; # $g is of type 'Graph' my @verts = $g->vertices; my @pairs = (); foreach my $front ( @verts ) { foreach my $back ( @verts ) { push @pairs, [ $front, $back ] if $g->has_edge( $front, $b +ack ); } } return @pairs; }
    (code untested)
    hope this helps!
Re: connected component
by water (Deacon) on May 14, 2005 at 18:56 UTC
    Some time later, my own home-rolled hack, using a breadth first search:
    package Algorithm::Graph; use strict; use Util qw(unique); # dedupes an array sub connected_components { my ($g) = @_; die "g must be arrayref" unless ref $g eq 'ARRAY'; die "empty graph?" unless @$g>0; my $adj; foreach my $pair (@$g) { die "g must be arrayref of arrayrefs" unless ref $pair eq 'ARR +AY'; die "g must be arrayref of 2-elem arrayrefs" unless @$pair == +2; my ( $x, $y ) = @$pair; $adj->{$x}{$y} = 1; $adj->{$y}{$x} = 1; } my %comp; for my $node ( keys %$adj ) { next if $comp{$node}; $comp{$node} = $node; my @neighbors = keys %{ $adj->{$node} }; while ( my $n = pop@ neighbors ) { die "set diff?" if $comp{$n} && $comp{$n} ne $node; $comp{$n} = $node; push( @neighbors, grep {! exists($comp{$_})} keys %{ $adj- +>{$n} } ); } } return [ map { my $c = $_; [ sort grep { $comp{$_} eq $c } keys %comp ]; } (sort (unique(values %comp))) ]; } 1;
    And the tests
    use strict; use warnings FATAL => 'all'; use Test::Exception; use Test::More tests => 12; use_ok('Algorithm::Graph'); my %graphs = ( '1,2' => [ [ 1, 2 ] ], 'a|b|c|d' => [ [qw(a a)], [qw(b b)], [qw(c c)], [qw(d d)] ], '1,2,3,4,99|a,b,c,d,e,f,g,z' => [ [qw(a b)], [qw(b c)], [qw(d c)], [qw(d e)], [qw(d f)], [qw(g d +)], [qw(z g)], [ 1, 2 ], [ 3, 2 ], [ 4, 3 ], [ 99, 1 ] ], '1,2,3,4,99|apple,kiwi,pear|a,b,c,d,e,f,g,z' => [ [qw(a b)], [qw(b c)], [qw(d c)], [qw(d e)], [qw(d f)], [qw(g d)], [qw(z g)], [ 1, 2 ], [ 3, 2 ], [ 4, 3 ], [ 99, 1 ], [ 'apple', 'pear' ], [ 'apple', 'kiwi' ] ], ); foreach my $result ( keys %graphs ) { my $cc = Algorithm::Graph::connected_components( $graphs{$result} +); my $cc2 = join( "|", map { join( ",", @$_ ) } @$cc ); is( $cc2, $result, $cc2 ); } throws_ok { Algorithm::Graph::connected_components() } qr/arrayref/, ' +empty'; throws_ok { Algorithm::Graph::connected_components('fish') } qr/arrayr +ef/, 'fish'; throws_ok { Algorithm::Graph::connected_components( {} ) } qr/arrayref +/, 'hash'; throws_ok { Algorithm::Graph::connected_components( [] ) } qr/empty/, +'[]'; throws_ok { Algorithm::Graph::connected_components( [ [] ] ) } qr/arra +yref/, '[[]]'; throws_ok { Algorithm::Graph::connected_components( [ [ 1, 2, 3 ] ] ) +} qr/2-elem/, '[[1,2,3]]'; throws_ok { Algorithm::Graph::connected_components( [ [ 1, 2 ], [] ] ) + } qr/2-elem/, '[[1,2],[]]';
    Your mileage may vary.

    water

      Representing graphs as edge sets is not sufficiently general, because edge sets alone cannot represent graphs that have isolated nodes. That's why the standard mathematical definition of a graph consists of both a set of nodes and a set of edges formed from nodes in the set of nodes.

      the lowliest monk

        an inelegant cheesy solution, but I had this case covered by bogus self <-> self links, as shown by the test on 4 iso nodes, which return as 4 iso components.
        # resulting components <--- graph 'a|b|c|d' => [ [qw(a a)], [qw(b b)], [qw(c c)], [qw(d d)] ],
        again, a cheesy handrolled quick hack for what I needed, is all.
Re: connected component
by tlm (Prior) on May 14, 2005 at 18:01 UTC

    Last time I needed to do this, which was a few years ago, I ended up rolling my own, because anything I could find in CPAN was too slow. This may have changed since then, but an algorithm for this is not difficult; see the attached code.

    the lowliest monk

Log In?
Username:
Password:

What's my password?
Create A New User
Domain Nodelet?
Node Status?
node history
Node Type: perlquestion [id://457074]
Approved by polettix
help
Chatterbox?
and the web crawler heard nothing...

How do I use this?Last hourOther CB clients
Other Users?
Others musing on the Monastery: (4)
As of 2024-04-25 06:56 GMT
Sections?
Information?
Find Nodes?
Leftovers?
    Voting Booth?

    No recent polls found