How can I make integer division in Perl OR How can I make my binary search work?

孤人 提交于 2019-12-11 09:28:35

问题


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.

  1. 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 program

    while($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;
        }   
    }
    
  2. 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

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!