从2列文件中查找群集

时间:2013-03-10 14:23:22

标签: perl

我有以下问题,我有一个类似它的文件:

1 2
1 3
2 4
2 5
6 7
6 8
9 1

每个数字代表网络的“节点”。左节点与右节点连接,如果它们连接,则它们属于同一“集群”。

我想找到这些数字和群集组成所产生的“群集”的数量,在这种情况下应该给出输出:

cluster[1]=(1,2,3,4,5,9)
cluster[2]=(6,7,8)

我认为为每个号码添加标签可能很有用,每次我找到这个号码的邻居或邻居的邻居时,它都会采用相同的标签(然后就是“第n”号码)在集群向量cluster[n])中,如果有一个数字不属于任何过去的集群,那么它需要一个新的标签等..但我不知道如何在代码中重现这个想法。有什么帮助吗?

3 个答案:

答案 0 :(得分:2)

如上所述,您应该使用Graph。您正在寻找无向图的连通组件。

#!/usr/bin/env perl

use strict;
use warnings;

use Graph::Undirected;
my $g = Graph::Undirected->new(unionfind => 1);

while (my $line = <DATA>) {
    last unless $line =~ /\A ([0-9]+) \s+ ([0-9]+) \s+ \z/x;
    $g->add_edge($1, $2);
}

my @cc = sort { @$b <=> @$a }
         map { [ sort @$_ ] } $g->connected_components;

printf "[%s]\n", join(', ', @$_) for @cc;

__DATA__
1 2
1 3
2 4
2 5
6 7
6 8
9 1
[1, 2, 3, 4, 5, 9]
[6, 7, 8]

答案 1 :(得分:1)

my @node_links = (
    {a => 1, b => 2},
    {a => 1, b => 3},
);

my %clusters;

for my $node_link (@node_links) {
    $clusters{ $node_link->{a} } ||= {};
    $clusters{ $node_link->{a} }->{ $node_link->{$b} } = 1;
    $clusters{ $node_link->{b} } ||= {};
    $clusters{ $node_link->{b} }->{ $node_link->{$a} } = 1;
}

my @clusters;

while (my($node, $node_links) = each %clusters) {
    my %cluster;
    $cluster{$node} = 1;
    delete $clusters{$node};
    build_cluster(\%clusters, \%cluster, $node_links);
    push(@clusters, \%cluster);
}

sub build_cluster {
    my($clusters, $cluster, $node_links) = @_;

    for my $node (keys %$node_links) {
        $cluster->{$node} = 1;
        if ($clusters->{$node}) {
            my $next_node_links = delete $clusters->{$node};
            build_cluster($clusters, $cluster, $next_node_links);
        }
    }
}

答案 2 :(得分:0)

除了此处提供的解决方案,您还可以使用Graph::UnionFind解决此问题。我认为最近我问过a question可能有帮助,因为它为同样的问题提供了很好的解决方案。