* @author Josh Bloch
* @author Neal Gafter
我想在Java 领域呆的比较长的开发者应该会有所耳闻吧,Josh Bloch 被称之为 "Java 之母"(虽然他是一位男性),因为 java collection 框架,java.math, 泛型 及《Effective Java Programming Language Guide》都出自他之手;而 Neal Gafter 则是 我们每天都用的JAVA编译器 Javac 的实现者。
我们先看有问题的代码:
public static int binarySearch(int[] a, int key) {
int low = 0;
int high = a.length-1;
while (low <= high) {
int mid = (low + high) >> 1;
int midVal = a[mid];
if (midVal < low =" mid"> key)
high = mid - 1;
else
return mid; // key found
}
return -(low + 1); // key not found.
}
有问题的代码是这一行:
int mid = (low + high) >> 1;
大家知道,其等价于:
int mid =(low + high) / 2;
问题是在 low 和 high 都很大时,比如数组的元素达到 2^30 时,low + high 将超过整数的最大值 2^31 -1,此时将造成溢流,溢流后得到的 mid 为负值。
正确的实现应该为:
int mid = low + ((high - low) / 2);
或者更加清楚地使用Java的无符号右移操作符:
int mid = (low + high) >>> 1;
虽然这个问题目前得到解决,我们就能断定这十几行程序就准确无误吗?
但连以上两位作者目前也还表示怀疑。
从行内得知,第一个二分法算法是1946年出现的,而当时被认为“无误”的实现到1962年才出现(也就是说以上十几行代码是经过十几年才得到的)。因为当时的数据量不可能逼近 2 ^ 30的数量级,所以直到去年这个BUG被提交到 Java 的 Bug 库中。可想人类的思维是有缺陷的。
目前,对于搜索引擎,基因工程领域,这一数量级应该是少见多怪了,所以如果你工作的领域需要处理大量的数据时,请使用 JDK 6.0
顺便提一句,对于C的实现,可以采用如下实现:
mid = ((unsigned) (low + high)) >> 1;看到这样的情况,作为程序员,我们应该时刻警惕、保持低调!
5 条评论:
好,我想转载您的帖子!呵呵,转载地址如下:http://hi.baidu.com/100do
如果您不同意转载,我将尽快取消转载。谢谢
很高兴你喜欢这篇文章!
欢迎转载!
P.P.Run
看到这样的文章,说明你是一个很谦虚的人。
事实上我们做任何事情都需要谦虚些,这样对自己今后的道路有好处。
在计算机领域,我们本身就比外国人起步晚,再者他们可以在8岁就接触这些东西,可是我们国家的孩子也许有这个年龄去碰电脑的,但是绝不可能去编程 ...
这个也不能说这么大吧. 任何事物都是在特定的历史条件下的, 超出历史限制的边界是来讨论一个纯概念没啥意义. 右移的速度比做除法要快很多的. 我们往往是在特定环境下做优化. 以此考虑, 当时的做法并没有任何不妥. 这个可以归结为事物发展进程中需要付出的代价.
发表评论