问题
I'm trying to implement binary search. This is my code:
#!/usr/bin/perl
#use strict;
use warnings;
@array = (1..100);
$number = <STDIN>;
$low = 0;
$high = $#array;
while($low < $high){
print "Searcing $low ---- $high \n";
$mid = $low + ($high - $low)/2;
if($array[$mid] == $number){
print "Found in index:" . $mid;
last;
}
elsif($array[$mid] < $number){
$low = $mid + 1;
}
else{
$high = $mid - 1;
}
}
But it does not work although it is a straight forward implementation (at least it would be in Java).
It seems that I get float values when dividing and can not search. If I provide as input 5
I get garbage:
5
Searcing 0 ---- 99
Searcing 0 ---- 48.5
Searcing 0 ---- 23.25
Searcing 0 ---- 10.625
Searcing 0 ---- 4.3125
Searcing 3.15625 ---- 4.3125
How can I make it use integer numbers so I can index the array?
Also if I uncomment use strict
I get the following errors. What do they mean?
Global symbol "@array" requires explicit package name at D:\Development\Perl\chapter3\binarySearch.pl line 6.
Global symbol "$number" requires explicit package name at D:\Development\Perl\chapter3\binarySearch.pl line 9.
Global symbol "$low" requires explicit package name at
回答1:
You should be using the function int.
Aside from that, you need to use strict;
and use my
to scope your variables. This will catch errors that you might miss. Just declare them like this:
my @array = (1..100);
chomp(my $number = <STDIN>);
my $low = 0;
my $high = $#array;
my $mid = int ($low + ($high - $low)/2);
You might consider using chomp too, to remove newlines from your input.
回答2:
my $int = int 5 / 2;
print $int;
Prints 2.
回答3:
Two esoteric solutions. Don't actually use these unless you are really bored or something.
use integer
-- a pragma that forces Perl to treat all your numerical values as integers. It can be locally scoped so it won't ruin all the data in your programwhile($low < $high){ print "Searcing $low ---- $high \n"; { use integer; $mid = $low + ($high - $low)/2; } if($array[$mid] == $number){ print "Found in index:" . $mid; last; } elsif($array[$mid] < $number){ $low = $mid + 1; } else{ $high = $mid - 1; } }
Perl has some special variables $=, $-, and $% that will only hold integer values, no matter what you assign to them. Use them directly
$- = $low + ($high - $low) / 2; if ($array[$-] == $number) { ...
or as intermediates
my $mid = $- = $low + ($high - $low) / 2; if ($array[$mid] == $number) { ...
Using the magic variables like this is handy for code golf and irritating your co-workers, but not much else.
回答4:
Use the good old >> 1
instead of /2
.
Example:
perl -e 'BEGIN { print 5/2, " : ", 5>>1}'
Output:
2.5 : 2
And use (spare an substraction):
my $mid = ($low + $high) >> 1;
回答5:
There are quite a few problems with your code.
You disabled strict.
Presumably because of the next problem.
You didn't declare any of your variables, with my or our or even use vars.
our @array = 1..100; use vars qw'$mid $low $high'; my $number = <STDIN>
You worry too much about overflows. This isn't C.
If your number would overflow an integer, it just becomes a float.
my $mid = $low + ($high - $low)/2;
Should probably just be:
my $mid = ($high + $low)/2;
You expected an integer out of a division.
my $mid = ($high + $low)/2;
If you really want an integer just use int.
my $mid = int( ($high + $low)/2 );
You didn't remove the newline from the end of
$number
chomp($number);
You have an off by one error.
while($low < $high){
Should really be
while($low <= $high){
This is really your main problem.
#! /usr/bin/env perl
use strict;
use warnings;
my @array = 1..100;
my $number = <STDIN>;
chomp $number;
my $low = 0;
my $high = $#array;
while($low <= $high){
my $mid = int( ($high + $low)/2 );
printf "Searching %2d .. (%2d) .. %2d\n", $low, $mid, $high;
if($array[$mid] == $number){
print "Found $number at index mid $mid\n";
last;
}elsif($array[$mid] < $number){
$low = $mid + 1;
}else{
$high = $mid - 1;
}
}
That's not really Perlish though.
#!/usr/bin/perl
use strict;
use warnings;
use List::MoreUtils qw'first_index';
my @array = 1..100;
my $number = <STDIN>;
chomp $number;
my $index = first_index { $_ == $number } @array;
print "Found $number at index ", $index if $index != -1;
Or more bizarrely
#! /usr/bin/env perl
use strict;
use warnings;
my @array = 1..100;
my $number = <STDIN>;
chomp $number;
my $char = join '', map chr, @array;
my $index = index $char, chr $number;
print "Found $number at index $index\n" if $index != -1;
This works with numbers up-to the lesser of UV
max or (2 ** 72) - 1
.
That is 18,446,744,073,709,551,615 on 64-bit builds, and 4,294,967,295 on 32-bit builds.
回答6:
First of all,
my $mid = $low + ($high - $low)/2
can be simplified to
my $mid = ($high + $low)/2;
You can get the integral part using int
my $mid = int( ($high + $low)/2 );
Or you could take advantage of the fact that a bit shift is an integer division by 2.
my $mid = ($high + $low) >> 1;
See also: Flexible Binary Search Function
来源:https://stackoverflow.com/questions/15935870/how-can-i-make-integer-division-in-perl-or-how-can-i-make-my-binary-search-work