算法二进制搜索返回范围perl

时间:2016-11-20 14:55:17

标签: arrays perl search binary

我有一个带有数百个浮点数(有序)的数组,然后是另一个较小的数组,我需要匹配一定容差内的数字(大数组中的值与数值中的值没有重叠)小数组)来自大数组中的小数组。好吧没什么大不了的,这是在容差范围内返回一个不完美匹配的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并做同样的事情直到上限?

1 个答案:

答案 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