我有一个带有数百个浮点数(有序)的数组,然后是另一个较小的数组,我需要匹配一定容差内的数字(大数组中的值与数值中的值没有重叠)小数组)来自大数组中的小数组。好吧没什么大不了的,这是在容差范围内返回一个不完美匹配的perl函数,它在for循环中,我循环遍历小数组值。
sub bin_search{
my ($arr, $v, $t ) = @_;
my ($min, $max) = (0, @$arr-1);
while ($min <= $max) {
my $w = $v + $t;
my $k = $v - $t;
my $try = int( ( $min + $max ) / 2 );
$min = $try + 1, next if $arr -> [$try] < $k ;
$max = $try - 1, next if $arr -> [$try] > $w ;
return $arr -> [$try] ;
}
return 0;
}
然后在检查我的数据之后,似乎我已经丢弃了一些值,因为它只返回第一个匹配。 我试过grep但是太慢了。
my $min = $val - $t;
my $max = $val + $t;
my @arr2 = grep { ( $_ > $min ) && ($_ < $max ) }@big_arr1;
所以我想修改二进制搜索以返回从$ min到$ max的范围,因为我认为只有一个匹配是$ min或$ max,所以像
sub bin_search{
my ($arr, $v, $t ) = @_;
my ($min, $max) = (0, @$arr-1);
my $w = $v + $t;
my $k = $v - $t;
while ($min <= $max) {
my $try = int( ( $min + $max ) / 2 );
$min = $try + 1, next if $arr -> [$try] < $k ;
$max = $try - 1, next if $arr -> [$try] > $w ;
last;
}
my @fin;
if ( ($arr -> [$try] < $w) && ($arr -> [$try] > $k) ) {
push @fin, $arr ->[$try]; $try++ }
return \@fin;
}
但是我错过了一些价值观,我认为我错过了一些东西,我当时应该只看一个方向吗?就像离开我们达到下限然后返回$ try并做同样的事情直到上限?
答案 0 :(得分:1)
首先使用二进制搜索找到匹配元素的索引。
完成后,您需要找到范围开始的位置。您也可以使用二分搜索,但如果匹配元素的数量通常较小,也可以使用线性搜索。
最后,您需要找到范围的结尾。您可以使用与查找范围起点相同的方法。
您的解决方案的问题在于您没有寻找范围的开头。
以下是使用线性扫描方法(与您的方法一样)的未经测试的实现,因此它假设匹配元素非常少:
sub binsearch_numeric_range {
my $min = shift;
my $max = shift;
my $array = shift;
return () if !@$array;
my $i = 0;
my $j = $#$array;
my $k;
while (1) {
$k = int(($i+$j)/2);
if ($array->[$k] > $max) {
$j = $k-1;
return () if $i > $j;
}
elsif ($array->[$k] < $min) {
$i = $k+1;
return () if $i > $j;
}
else {
last;
}
}
my $min_k = $k; --$min_k while $min_k > 0 && $array->[$min_k - 1] >= $min;
my $max_k = $k; ++$max_k while $max_k < $#$array && $array->[$max_k + 1] <= $max;
return @$array[$min_k .. $max_k];
}
my @matches = binsearch_numeric_range($v-$t, $v+$t, $arr);
不需要编写全新的binsearch
:
my $idx = binsearch { abs($a-$b) <= $t ? 0 : $a <=> $b } $v, @$arr;
my @range;
if ($idx >= 0) {
my $min_idx = $idx; --$min_idx while $min_idx > 0 && $arr->[$min_idx-1] >= ($v-$t);
my $max_idx = $idx; ++$max_idx while $max_idx < $#$arr && $arr->[$max_idx+1] <= ($v+$t);
@range = @$array[$min_idx .. $max_idx];
}
使用的binsearch
定义为here。