如果对水果进行排序,两次二分搜索会很快找到水果。
sub search_cmp
my @fruits = (
{ name => "Orange", price => 40, ... },
...
);
my @ranges = (
[ 35, 55 ],
...
);
my @sorted_fruits = sort { $a->{price} <=> $b->{price} } @fruits;
for my $range (@ranges) {
my $i = binsearch { $a <=> $b->{price} } $range[0], @sorted_fruits, 0;
$i = ~$i if $i < 0;
my $j = binsearch { $a <=> $b->{price} } $range[1], @sorted_fruits, $i;
$j = ~$j - 1 if $j < 0;
say "[$range->{min}, $range->{max}]: @fruits[$i..$j]";
}
sub _unsigned_to_signed { unpack('j', pack('J', $_[0])) }
sub binsearch(&$\@;$$) {
my $compare = $_[0];
#my $value = $_[1];
my $array = $_[2];
my $min = $_[3] // 0;
my $max = $_[4] // $#$array;
return -1 if $max == -1;
my $ap = do { no strict 'refs'; \*{caller().'::a'} }; local *$ap;
my $bp = do { no strict 'refs'; \*{caller().'::b'} }; local *$bp;
*$ap = \($_[1]);
while ($min <= $max) {
my $mid = int(($min+$max)/2);
*$bp = \($array->[$mid]);
my $cmp = $compare->()
or return $mid;
if ($cmp < 0) {
$max = $mid - 1;
} else {
$min = $mid + 1;
}
}
return _unsigned_to_signed(~$min);
}
性能分析
最好的最坏情况是 O(R * F),因为每个范围都可以匹配每个水果。
要求替换的 OP 描述的幼稚方法是 O(R * F)。那么它是否尽可能快?不,因为天真的方法总是采用更糟糕的情况。
在实践中,如果我们可以假设每个范围只匹配几个水果,我们可以从上面得到更好的平均结果:O( ( F + R ) log F )