Warning: file_get_contents(/data/phpspider/zhask/data//catemap/9/java/307.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181

Warning: file_get_contents(/data/phpspider/zhask/data//catemap/4/algorithm/12.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Java 修复Bentley'的二进制搜索错误;s书(编程珍珠:编写正确的程序)_Java_Algorithm_Overflow_Binary Search - Fatal编程技术网

Java 修复Bentley'的二进制搜索错误;s书(编程珍珠:编写正确的程序)

Java 修复Bentley'的二进制搜索错误;s书(编程珍珠:编写正确的程序),java,algorithm,overflow,binary-search,Java,Algorithm,Overflow,Binary Search,二进制搜索可以通过许多方式实现,如递归、迭代、条件等。我从Bentley的书《编程珍珠》(Programming pearls):编写正确的程序)中得到了这一点,这是一种迭代实现,其中包含一个bug public class BinSearch { static int search( int [] A, int K ) { int l = 0; int u = A. length -1; int m;

二进制搜索可以通过许多方式实现,如递归、迭代、条件等。我从Bentley的书《编程珍珠》(Programming pearls):编写正确的程序)中得到了这一点,这是一种迭代实现,其中包含一个bug

 public class BinSearch 
    {
       static int search( int [] A, int K ) {
          int l = 0;
          int u = A. length -1;
          int m;
          while ( l <= u ) {
              m = (l+u) /2;
              if (A[m] < K){
              l = m + 1;
              } else if (A[m] == K){
                    return m;
              } else {
                    u = m-1;
              }
         }
    return -1;
    }
}
公共类b搜索
{
静态整数搜索(int[]A,int K){
int l=0;
int u=A.长度-1;
int m;
而(l尝试以下方法:

改变

m=(l+u)/2

m=(u-l)/2+l

< <代码>(L+U)/2 溢出的原因变得非常明显,如果您考虑一个非常大的2 ^ 31到1个元素数组(最大值是一个带符号的32位整数可以保存)。 在这种情况下,第一次迭代是很好的,因为<代码> 2 ^ 31 - 1 + 0 < /代码>不是很大,但是考虑这里的代码< >代码=M + 1 < /COD>这里。在第二次迭代中,U仍然是相同的,L是代码> 2 ^ 31/2 < /代码>,所以代码> L+U 会导致溢出。

通过这种方式,我们避免了添加
u+l
,首先确定l和u
(u-l)/2之间的相对中间值,然后将较低的数字l添加到其中,使其成为绝对值。因此,在操作期间
m=(u-l)/2+l;
我们从不超过u的值

要总结完整的代码,请执行以下操作:

public class BinSearch 
{
    static int search( int [] A, int K ) 
    {
        int l = 0;
        int u = A. length -1;
        int m;

        while ( l <= u ) 
        {
            m = (u-l) / 2 + l;

            if (A[m] < K)
                l = m + 1;
            else if (A[m] == K)
                return m;
            else
                u = m - 1;
        }
        return -1;
     }
}
公共类b搜索
{
静态整数搜索(int[]A,int K)
{
int l=0;
int u=A.长度-1;
int m;

而(l我认为你应该把u=m-l;改为u=m-1

不是我。 ---------添加------ 原因(l+u)可能大于2^31-1(如果整数为32位),因此可能会溢出。因此,您应该将(l+u)/2更改为l+((u-l)>>1),并且u-l不能大于2^31-1。

尝试更改

m = (l+u) / 2


很容易看出两者相等,而且第二个语句也可以防止溢出。

假设l和u都是int,都落入[0,2^31-1]中。 如果l,u>=2^30,则(l+u)>=2^31为溢出。若要避免此情况,请使用

m = l + (u-l)/2; 
相反,更重要的是,这样写二进制搜索可能更合理:

    public class BinSearch 
    {
        static int search( int [] A, int K ) {
            int l = -1;             // index lower bound shift left by 1
            int u = A.length;       // index upper bound shift right by 1
            int m;
            while ( l + 1 < u ) {
                m = l + (u-l)/2;    // avoid overflow
                if (A[m] < K){
                    l = m;          // keep A[l] < K 
                } else {
                    u = m;          // keep A[u] >= K
                }
            }
            if ( (u == A.length) || (A[u] != K) ) return -1;
            return u;
        }
    }
公共类b搜索
{
静态整数搜索(int[]A,int K){
int l=-1;//索引下限左移1
int u=A.length;//索引上限右移1
int m;
而(l+1=K
}
}
如果((u==A.length)|(A[u]!=K))返回-1;
返回u;
}
}

正如其他一些人所说,修复很简单,当然是我见过的100点悬赏中最简单的一个!下面是另一个,它具有很好的对称性,即使它需要更多的时钟周期:

m = (l >> 1) + (u >> 1) + (l & u & 1);
除非你有更好的信息,否则你不应该诽谤宾利是个“臭虫”。当他为ACM写那篇文章时(我想是在20世纪80年代的某个时候),他是在用32位C进行伪编码和写入;内存为GB的机器不存在。即使有4字节的整数,32位机器也不能有超过2^28整数的数组。因此,最高可能的索引是2^28-1。将该值加倍不会导致in
int
溢出

当然,这与32位Java完全相同。您需要64位Java的破烂组合—一种允许大小接近2^64的对象,但将索引限制为2^32-1的语言,以便出现此“bug”


你所说的缺陷是操作假设的改变。如果环境以正确的方式改变,宇宙中的每个程序都会表现出某种缺陷。

迭代实现二进制搜索确实有缺陷。change
m=(l+u)/2
。如其他人所述,它可能导致整数溢出。用
m=l+(u-l)/2
替换


根据我的经验,我已经一次又一次地看到了错误的二进制搜索实现。虽然二进制搜索是一个涉及分治的简单概念,但可能很难正确实现。更改上面的
m
赋值很容易。希望这能帮助…

尽管Machtl和其他人已经编写了克服溢出错误的方法但是为了完整性而添加

  • 使用intmid=(低+高)>>1;更快的方法
  • 和C和C++(我们没有操作符) mid=((无符号整数)低+(无符号整数)高))>>1;
    不应该
    while(我从《编程珍珠:编写正确的程序》一书中得到它。
    U
    将是未知的标识符,
    U
    更有意义。你没有试过编译它吗?这个问题是几年前发现的-一些分析存在于通常设置为m=l+(U-l)的位置/2;很好。二进制搜索的关键原则是在二进制操作中将间隔减半时保持先决条件不变。从技术上讲,此问题可能发生在
    2^30(+1?
    2^31-1
    元素之间的数组上。为什么不“m=l/2+u/2”这也会防止溢出,是吗?但是,分操作是两次。你怎么说?“斯大林考虑L,U都是奇数的情况。”NETANEWW:是的,我没有想到。谢谢。这本书的读者将包括使用16位整数的嵌入式设备的编码器。我称它为BUG,特别是因为它在里面。解释性的形式。我猜宾利也会。
    m = (l >> 1) + (u >> 1) + (l & u & 1);