我正在使用perl编写,但它似乎更像是一个算法问题。欢迎使用其他语言的回复。
我有两个排序的整数数组,short
和long
。对于short
中的每个元素,我想在long
中找到最接近的元素,在我的特定情况下,我想要制作距离的直方图。
这是我正在使用的算法:
sub makeDistHist {
my ($hist, $short, $long, $max) = @_; # first 3 are array references
my $lIndex = 0;
foreach my $s (@$short) {
my $distance = abs( $s - $long->[$lIndex] );
while (abs( $s - $long->[$lIndex+1] ) < $distance) {
$distance = abs( $s - $long->[$lIndex] );
$lIndex++;
}
$distance = $max if $distance>$max; # make overflow bin
$hist->[$distance]++;
}
}
这取决于short
和long
的排序。
这是我编写的用于测试算法的子程序。第一次测试成功,但第二次测试失败:
sub test { # test makeDistHist
my @long = qw(100 200 210 300 350 400 401 402 403 404 405 406);
my @short = qw(3 6 120 190 208 210 300 350);
my @tarHist;
$tarHist[97]++;
$tarHist[94]++;
$tarHist[20]++;
$tarHist[10]++;
$tarHist[2]++;
$tarHist[0]+=3;
my $max = 3030;
my @gotHist;
makeDistHist(\@gotHist, \@short, \@long, $max);
use Test::More tests => 2;
is_deeply(\@gotHist, \@tarHist, "did i get the correct distances for two different arrays?");
@gotHist = ();
@tarHist = ( @long+0 );
makeDistHist(\@gotHist, \@long, \@long, $max);
is_deeply(\@gotHist, \@tarHist, "did i get the correct distances between an array and itself?"); # nope!
print Dumper(\@gotHist);
}
这是转储:
$VAR1 = [
7,
5
];
(如果我将long
与其副本减去一个元素进行比较,则问题仍然存在,因此算法要求short
严格要比long
严格要短。我将401,402 ...更改为402,404 ... gotHist
变为(7, undef, 5)
。)
以下是我们所喜欢的内容:首先,这是一个有效的算法。要么修复我所拥有的,要么从整个布料中设计另一个。其次,我可以在调试技巧方面使用帮助。您将如何使用现有算法识别问题?如果我能这样做,我就不需要问这个问题:)
谢谢!
答案 0 :(得分:3)
你应该打破子程序:计算距离和构建直方图是两回事,试图将两者结合起来会失去很多清晰度。
首先从最简单的解决方案开始。我通过使用排序的@long
了解潜在的优化,但只有在List::Util::min速度缓慢的情况下才能使用它。
您可以使用Statistics::Descriptive生成频次分配。
#!/usr/bin/perl
use strict; use warnings;
use List::Util qw( min );
use Statistics::Descriptive;
my $stat = Statistics::Descriptive::Full->new;
my @long = (100, 200, 210, 300, 350, 400, 401, 402, 403, 404, 405, 406);
my @short = (3, 6, 120, 190, 208, 210, 300, 350);
for my $x ( @short ) {
$stat->add_data(find_dist($x, \@long));
}
my $freq = $stat->frequency_distribution_ref([0, 2, 10, 20, 94, 97]);
for my $bin ( sort { $a <=> $b } keys %$freq ) {
print "$bin:\t$freq->{$bin}\n";
}
sub find_dist {
my ($x, $v) = @_;
return min map abs($x - $_), @$v;
}
输出:
[sinan@archardy so]$ ./t.pl 0: 3 2: 1 10: 1 20: 1 94: 1 97: 1
当然,可以不使用任何模块并使用您对已排序@long
的假设来执行此操作:
#!/usr/bin/perl
use strict; use warnings;
my @long = (100, 200, 210, 300, 350, 400, 401, 402, 403, 404, 405, 406);
my @short = (3, 6, 120, 190, 208, 210, 300, 350);
my @bins = reverse (0, 2, 10, 20, 94, 97);
my %hist;
for my $x ( @short ) {
add_hist(\%hist, \@bins, find_dist($x, \@long));
}
for my $bucket ( sort { $a <=> $b } keys %hist ) {
print "$bucket:\t$hist{$bucket}\n";
}
sub find_dist {
my ($x, $v) = @_;
my $min = abs($x - $v->[0]);
for my $i ( 1 .. $#$v ) {
my $dist = abs($x - $v->[$i]);
last if $dist >= $min;
$min = $dist;
}
return $min;
}
sub add_hist {
my ($hist, $bins, $x) = @_;
for my $u ( @$bins ) {
if ( $x >= $u ) {
$hist{ $u } += 1;
last;
}
}
return;
}
答案 1 :(得分:0)
关于有关调试的部分,请使用允许断点的IDE。我没有perl的例子,但是对于PHP和ASP.NET,分别有Eclipse和Visual Studio(或免费版本,Visual Web Developer)。