【问题标题】:How can I make integer division in Perl OR How can I make my binary search work?如何在 Perl 中进行整数除法或如何使我的二进制搜索工作?
【发布时间】:2013-04-10 20:35:07
【问题描述】:

我正在尝试实现二进制搜索。这是我的代码:

#!/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;
    }   
}

但它不起作用,尽管它是一个直接的实现(至少它会在 Java 中)。
似乎我在划分时得到浮点值并且无法搜索。如果我提供 5 作为输入,我会得到垃圾:

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  

如何使它使用整数以便我可以索引数组?
此外,如果我取消注释 use strict 我会收到以下错误。他们的意思是什么?

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 

【问题讨论】:

    标签: perl int binary-search


    【解决方案1】:
    my $int = int 5 / 2;
    print $int;
    

    打印 2。

    【讨论】:

    • 这是转换为整数吗?
    • 还有什么是my?我需要吗?这是使用strict时出错的原因吗?
    • perldoc.perl.org/strict.htmlperldoc.perl.org/functions/my.html 基本上,使用严格和正确本地化变量是一个好习惯。你需要它吗?不。你应该使用它,是的。
    • @Cratylus - 你已经在另一个 post 中被告知 my 今天是什么
    • @chrsblck:过去几天我一直在阅读Perl。我还没有得到大部分内容,所以我再次请求得到它
    【解决方案2】:

    两个深奥的解决方案。除非您真的很无聊或其他什么,否则不要实际使用这些。

    1. use integer -- 强制 Perl 将所有数值视为整数的编译指示。它可以在本地范围内,因此不会破坏程序中的所有数据

      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 有一些特殊的变量$=$-$%,无论你分配给它们什么,它们都只会保存整数值。直接使用它们

      $- = $low + ($high - $low) / 2;
      if ($array[$-] == $number) { ...
      

      或作为中间体

      my $mid = $- = $low + ($high - $low) / 2;
      if ($array[$mid] == $number) { ...
      

      使用像这样的魔法变量对code golf 很方便,并且会激怒你的同事,但除此之外就没什么了。

    【讨论】:

    • 为什么 Perl 在用作数组索引时不将数值视为整数?我的意思是在我的 OP 中,我假设打印的 48.5 不用作 48 到索引数组,这就是二进制搜索不起作用的原因
    • 确实如此。 $array[48.5] 将返回与 $array[48] 相同的值。
    • 那么为什么代码只有在我使用int() 作为mid 时才有效?
    • 因为否则$high$low 可能包含浮点值,您的停止条件$high &gt;= $low 将永远不会得到满足。
    【解决方案3】:

    您应该使用函数int

    除此之外,您还需要 use strict; 并使用 my 来确定变量的范围。这将捕获您可能错过的错误。只需像这样声明它们:

    my @array = (1..100);
    chomp(my $number = <STDIN>);
    my $low = 0;
    my $high = $#array;
    
    my $mid = int ($low + ($high - $low)/2);
    

    您也可以考虑使用chomp,从输入中删除换行符。

    【讨论】:

    • 所以my 缺失是它不能在strict 上运行的原因,对吧?
    • @Cratylus 是的,这就是为什么它抱怨它需要一个明确的包名称。不过要学会爱上它,因为strict 确实能捕捉到您可能会忽略的错误。
    • 其实 int 不是强制转换操作符,它是一个函数。因为在 perl 中没有严格的变量类型,所以不需要强制转换运算符。
    【解决方案4】:

    使用旧的&gt;&gt; 1 而不是/2

    例子:

    perl -e 'BEGIN { print 5/2, " : ", 5>>1}'
    

    输出:

    2.5 : 2
    

    并使用(备用减法):

    my $mid = ($low + $high) >> 1;
    

    【讨论】:

    • 这是一个已签名的部门吗?是否有无符号除法的符号? (如&gt;&gt;&gt; in Java
    • @Cratylus:不。您可以使用int -5/2 版本。
    【解决方案5】:

    你的代码有很多问题。

    • 您禁用了strict

      大概是因为下一个问题。

    • 您没有声明任何变量,myour 甚至 use vars

      our @array = 1..100;
      use vars qw'$mid $low $high';
      my $number = <STDIN>
      
    • 你太担心溢出了。这不是 C。

      如果你的数字会溢出一个整数,它就会变成一个浮点数。

      my $mid = $low + ($high - $low)/2;
      

      应该只是:

      my $mid = ($high + $low)/2;
      
    • 您希望除法中的整数。

      my $mid = ($high + $low)/2;
      

      如果你真的想要一个整数,只需使用int

      my $mid = int( ($high + $low)/2 );
      
    • 您没有从$number 末尾删除换行符

      chomp($number);
      
    • 你有一个错误。

      while($low < $high){
      

      应该是

      while($low <= $high){
      

      这确实是你的主要问题。

    #! /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;
        }
    }
    

    不过,这并不是真正的 Perlish。

    #!/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;
    

    或者更奇怪的是

    #! /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;
    

    这适用于不超过UV max 或(2 ** 72) - 1 中较小者的数字。
    在 64 位版本上为 18,446,744,073,709,551,615,在 32 位版本上为 4,294,967,295。

    【讨论】:

    • +1.我没有得到这个 If your number would overflow an integer, it just becomes a float. 和 perlish 的例子对我来说完全无法理解(Perl 新手)
    • @Cratylus 第一个 Perlish 示例只是遍历列表,返回它变为真的索引。第二个奇怪的 Perl 示例利用了 Perl 的 UTF8 样式字符串处理能力。 (实际上不是很 Perlish)
    • first_index { $_ == $number } 这是一个内联函数吗?
    • @Cratylus first_indexgrep 类似。
    【解决方案6】:

    首先,

    my $mid = $low + ($high - $low)/2
    

    可以简化为

    my $mid = ($high + $low)/2;
    

    您可以使用int获取积分部分

    my $mid = int( ($high + $low)/2 );
    

    或者您可以利用位移是整数除以 2 这一事实。

    my $mid = ($high + $low) >> 1;
    

    另见:Flexible Binary Search Function

    【讨论】:

    • $low+($high-$low)/2 是 C 中的一个习惯用法,用于处理 $low+$high 的结果大于最大 int 值的情况。它在 Perl 中可能没那么有用。
    • @mob,是的,如果您可以拥有一个元素数量超过可寻址空间一半的数组,这只是一个问题。这在 Perl 中不会发生,因为每个元素至少需要 sizeof(void*) 字节。即使您忽略这一点,您也不会遇到 32 位 Perl 构建的问题,因为 253 或 264(数字范围)远大于 2**33(2 * 索引范围)。如果您有一个包含约 10,000,000,000,000,000,000 个或更多元素的数组(正如我所说,这在 Perl 中不会发生),您可能会在 64 位构建中遇到问题。
    • @ikegami:我认为这个答案和你的评论是不正确的。我使用low + (high - low)/2 来避免溢出。我相信这应该可能发生在Perl,因为我们可以通过索引数组int 仅索引已签名并具有 2^32 - 1 可寻址元素。所以我不确定你在评论中对number of elements is more half of the addressable space etc 的意思
    • @ikegami:也关于($high + $low) &gt;&gt; 1;这是有符号还是无符号的划分?
    • @Cratylus,目前,未签名。
    猜你喜欢
    • 1970-01-01
    • 2010-10-07
    • 2021-12-13
    • 1970-01-01
    • 2013-04-20
    • 2015-03-30
    • 1970-01-01
    • 1970-01-01
    • 2016-05-15
    相关资源
    最近更新 更多