0

这是我的数据

nodes             x             y         z

1112,         23,       56,       88
2223,         56,       78,       54
3345,         32,       12,       11
4321,         11,       10,       06
5234,         10,       10,       12
6123,         12,       06,       04

现在我想要那些彼此最近的节点..例如:如果节点(1112)和节点(4321)之间的距离小于(让d = 10)节点(1112)和节点(3345)之间的距离(让d=34),反之亦然,输出应为=

     node:1112 and node:4321 are closest
     node:6123 and node:3345 are closest.....

我正在使用这样的距离公式: my $dist = sqrt( ($x-$x2)* 2 + ($y-$y2) *2 + ($z-$z2)**2 );

 i.e:
 d=sqrt( (56-23)**2 + (78-56)**2 + (54-88)**2 )
 d=sqrt( (32-23)**2 + (12-56)**2 + (11-88)**2 )
 d=sqrt( (11-23)**2 + (10-56)**2 + (06-88)**2 )
 d=sqrt( (10-23)**2 + (10-56)**2 + (12-88)**2 )....and so on

对于其他节点及其坐标类似

 d=sqrt( (23-56)**2 + (56-78)**2 + (88-54)**2 )
 d=sqrt( (32-56)**2 + (12-78)**2 + (11-54)**2 )
 d=sqrt( (11-56)**2 + (10-78)**2 + (06-54)**2 )
 d=sqrt( (10-56)**2 + (10-78)**2 + (12-54)**2 )....

我已经这样做了,但一无所获......

use strict;
use warnings;
use Data::Dumper;

open(IN , "<" , "1.txt");
#open(OUT , ">" , "out.txt");

my $node_flag=0;
my %node_hash_1;
my %node_hash_2;

my @node_1;
my @node_2;

my @distance_array;
my $i=0;

foreach my $line(<IN>)
{
    if($line=~/^\*node$/i)
    {
        $node_flag=1;
        next;
    }
    if($node_flag==1)
    {
        if($line!~/^\*\*/i)
        {
            my @array=split(",",$line); # or  my @array=(split(",",$line))[1,2,3]
            my $node_id=shift @array;
            $node_hash_1{$node_id}=\@array;

            push(@node_1,[@array]);
            push(@node_2,[@array]);

            foreach my $var1(@node_1)
            {
                my($x,$y,$z)=@{$var1};
                foreach my $var2(@node_2)
                {
                    my($x2,$y2,$z2)=@{$var2};
                    my $dist = sqrt( ($x-$x2)**2 + ($y-$y2)**2 + ($z-$z2)**2 );
#                   print $dist;
                    print"\n";
                    foreach my $value(@{$node_hash_1{$node_id}})
                    {
                        my $vars=join",",@{$node_hash_1{$node_id}};
                        my @arr=split ",",$vars;
#                       print $arr[0];
                        print $vars;
                        if ($x==$value && $y==$value && $z==$value)
                        {
                            push @{$node_hash_1{$node_id}},$dist;
#                           $node_hash_2{x} = $x;
#                           $node_hash_2{y} = $y;
#                           $node_hash_2{z} = $z;
                        }
                    }
                }
            }
        }
        else
        {
            $node_flag=0;
        }
    }
}
#print Dumper(\%node_hash);
#print Dumper(\@distance_array);
#print Dumper(\@node_1);
#print Dumper(\%node_hash_2);
4

2 回答 2

5

输入行从不匹配'*node',因此$node_flag永远不会设置,因此第二个如果完全跳过,则跳过所有内容。

  • 由于'nodes'带有一个's'并且出现在第一行的开头,我认为您没有正确使用它。
  • 此外,由于我在您的输入中没有看到文字 '*',我认为您不明白这/\*/是匹配文字星的一种方式。

我将正则表达式修改为 read m/^nodes\b/,它至少设置了 $node_flag 并让我进入主体。事情是这样的,但如果您知道文件顶部会出现一定数量的行,并且您不需要该信息,则可以这样做:

<IN> foreach 1..2; 
# or just once if that blank line is just a formatting mistake.

如果您想检查标题行:

die 'No Header Line!' unless <IN> =~ m/^nodes\b/;

这样,您就不会进入循环(while无论如何它应该是一个循环)。

  • 您将相同的坐标推入两个数组,表明您过度使用了foreach循环。

  • 此外,在收集数据时进行笛卡尔比较可以以更好的方式完成,如果您当前输入值保留在列表之外,将其与已存储在列表中的成员进行比较。看起来您最终将节点 #1 与节点 #1 进行了尽可能多的比较。

虽然如果我真的关心数据,我可能会将其分解为一组类,但我提供以下更改作为直线 perl 处理器:

my ( @node_list, %distance_between, %closest_to );

die 'No Header' unless <IN> =~ m/^nodes\b/;

while ( my $line = <IN> ) {
    my ( $name, $x, $y, $z ) = split /\s*,\s*/, $line;
    foreach my $comp_node ( @node_list ) {
        my ( $name2, $x2, $y2, $z2 ) = @$comp_node;

        # distance_between is a needless structure, if 
        # you don't want to keep the distance. 
        $distance_between{ $name2 }{ $name } 
            = $distance_between{ $name }{ $name2 } 
            = my $dist
            = sqrt( ($x-$x2)**2 + ($y-$y2)**2 + ($z-$z2)**2 )
            ;

        my $closest = $closest_to{ $name2 } ||= [ $name, $dist ];
        if ( $closest->[1] > $dist ) { 
            $closest_to{ $name2 } = [ $name, $dist ];
        }
        $closest = $closest_to{ $name } ||= [ $name2, $dist ];
        if ( $closest->[1] > $dist ) { 
            $closest_to{ $name } = [ $name2, $dist ];
        }
    }
    push @node_list, [ $name, $x, $y, $z ];
}
foreach my $p ( map { [ $_, $closest_to{ $_ }[0] ] } sort keys %closest_to ) {
    say "$p->[0]'s closest neighbor is $p->[1]" ;
}
于 2012-08-09T16:08:48.033 回答
2
#!/usr/bin/perl

use strict;
use warnings;

my (@data, @temp, @result);

sub calc {
    my ($a, $b) = @_;
    my $dat = 0;
    $dat += ($a->[$_] - $b->[$_]) ** 2 for (1..3);
    return sqrt $dat;
}

while (<DATA>) {
    push @data, [split /,?\s+/];
}

for my $x (@data) {
    for my $y (@data) {
        push @temp, [calc($x, $y), $x, $y] if ($x->[0] > $y->[0]);
    }
}

@result = sort { $a->[0] <=> $b->[0]; } @temp;

for (@result) {
    print "[", join(', ', @{$_->[1]}), "], [", join(', ', @{$_->[2]}),
        "] => $_->[0]\n";
}

__DATA__
1112,         23,       56,       88
2223,         56,       78,       54
3345,         32,       12,       11
4321,         11,       10,       06
5234,         10,       10,       12
6123,         12,       06,       04

输出:

[6123, 12, 06, 04], [4321, 11, 10, 06] => 4.58257569495584
[5234, 10, 10, 12], [4321, 11, 10, 06] => 6.08276253029822
[6123, 12, 06, 04], [5234, 10, 10, 12] => 9.16515138991168
[4321, 11, 10, 06], [3345, 32, 12, 11] => 21.6794833886788
[6123, 12, 06, 04], [3345, 32, 12, 11] => 22.0227155455452
[5234, 10, 10, 12], [3345, 32, 12, 11] => 22.113344387496
[2223, 56, 78, 54], [1112, 23, 56, 88] => 52.2398315464359
[3345, 32, 12, 11], [2223, 56, 78, 54] => 82.3468275041607
[3345, 32, 12, 11], [1112, 23, 56, 88] => 89.1403387922662
[5234, 10, 10, 12], [1112, 23, 56, 88] => 89.7830719011106
[5234, 10, 10, 12], [2223, 56, 78, 54] => 92.217135067188
[4321, 11, 10, 06], [2223, 56, 78, 54] => 94.62029380635
[4321, 11, 10, 06], [1112, 23, 56, 88] => 94.7839648885823
[6123, 12, 06, 04], [2223, 56, 78, 54] => 98.0815986819138
[6123, 12, 06, 04], [1112, 23, 56, 88] => 98.3717439105356
于 2012-08-09T17:01:36.517 回答