关于binary search的一点解惑
在写binary search时对于mid的计算我最开始使用的是
mid = (low + high)/2;
后来看到在很多的实现为
mid = low + (high - low)/2;
想了一下两种实现貌似没什么差别啊,后来无意看到g9博客上的一篇文章,备份一下:
http://blog.csdn.net/g9yuayon/article/details/772382
这是Joshua Bloch(Effective Java的作者)在google blog上发的帖子。在说这个帖子之前,不得不强力重复Joshua Bloch的推荐:如果你还没有读过Programming Pearls (中文版叫《编程珠玑》)这本书,现在就去读吧。如果你只读了一遍,现在就去再读一遍吧。
还是说回Joshua的文章。当初Programming Pearls的作者Jon Bentley到CMU做讲座。他叫在场的计算机系博士生们写出binary search的算法,然后当场分析了其中一份。当然,那份算法以及绝大部分人写的算法都错了。Jon Bentley在Programming Pearls里也提到,虽然1946年就有人发表binary search,但直到1962第一个正确运行的算法才写出来。这个小故事的关键教训就是写程序时要仔细考虑算法的不变量(invariant)。如果我记 得没错,Programming Pearls第4章讲解了怎么证明binary search的正确性。当然,每本离散数学的教科书都会教我们列出pre-condition, invariant, 和post-condition,证明循环开始前pre-condition成立,循环中invariant始终成立,而循环结束后post- condition被满足,而几乎每本教科书(至少我看过的)都会用binary search作例子。所以有兴趣的自己去看吧,俺就不罗嗦了。
JDK里的binary search代码是这样实现的(Joshua Bloch本人写的)
1: public static int binarySearch(int[] a, int key) {
2: int low = 0;
3: int high = a.length - 1;
4:
5: while (low <= high) {
6: int mid = (low + high) / 2;
7: int midVal = a[mid];
8:
9: if (midVal < key)
10: low = mid + 1;
11: else if (midVal > key)
12: high = mid - 1;
13: else
14: return mid; // key found
15: }
16: return -(low + 1); // key not found.
17: }
错误就在第6行:
6: int mid = (low + high) / 2;
这行的问题是当low和high的和超过2^31-1, 也就是Java里最大整数值时,整数溢出就发生了,而mid就变成负数了, 于是JVM就抓狂了,于是ArrayIndexOutOfBoundsException就发生了。
当一个数组包含多过2^30元素时,这个错误就会被发现。那么大的数组在80年代
Programming Pearls第一版写就的时候难以想象,但在现在却很常见。所以说,尽管1962年正确的binary
search问世,现实却是直到现在流行系统里的binary search还有错。
解决的办法不难。把第6行改写成
6: int mid = low + ((high - low) / 2);
或者
6: int mid = (low + high) >>> 1;
C和C++里没有这个">>>",我们可以这样做:
6: int mid = ((unsigned) (low + high)) >> 1。
那现在binary search就完全正确了么?我们还是不知道。我们得到的深刻教训是,仅仅证明一个程序正确是不够的。我们必须仔细测试。高德纳在写给Peter van Emde Boas的信里说,“上面那段程序可能有错。我只证明了它是正确的,但还没有测过”。人们往往用这段话来彰显高德纳的一丝不苟和学究气,谁知道这句话背后是高德纳深刻的洞察力。人们常说“理论上讲实践和理论没有差别。实践上讲,两者确有差别”,可为旁证。
binary search的这个错误同样会出现在其它“分而治之”的算法里,比如说mergesort。如果你有类似的算法代码,赶快修改吧。Joshua说,他从中学到的教训是谦卑:哪怕一个简单的程序都很难写对,而整个社会却运行在庞大而复杂的代码上面。
最后的总结很有意思:我们程序员需要各种帮助,别无它法。仔细设计很好。测试很好。形式化方法
很好(不过我还是觉得有教授研究用形式化电子商务需求(比如用范畴论),纯粹无事找事)。代码评审很好,静态分析很好。但他们并不能帮我们彻底消除代码错
误--他们将永远存在。我们半个世纪以来竭尽全力都不能消除一个程序错误。我们必须小心翼翼,防御性地编程,并且保持警醒。
关于binary search的一点解惑的更多相关文章
- BINARY SEARCH 的一点说明
在sap 之abap语言中,有BINARY SEARCH这个查找条件.使用read table 来读取内表时,使用BINARY SEARCH可以大大的提高查找的效率,为什么呢?学过数据库的人会知道 ...
- [LeetCode] Verify Preorder Sequence in Binary Search Tree 验证二叉搜索树的先序序列
Given an array of numbers, verify whether it is the correct preorder traversal sequence of a binary ...
- Inorder Successor in Binary Search Tree
Given a binary search tree (See Definition) and a node in it, find the in-order successor of that no ...
- 【转】STL之二分查找 (Binary search in STL)
Section I正确区分不同的查找算法count,find,binary_search,lower_bound,upper_bound,equal_range 本文是对Effective STL第4 ...
- [LeetCode]题解(python):098 Validate Binary Search Tree
题目来源 https://leetcode.com/problems/validate-binary-search-tree/ Given a binary tree, determine if it ...
- LeetCode----Unique Binary Search Trees 2
Given n, generate all structurally unique BST's (binary search trees) that store values 1...n. For e ...
- Binary Search
Binary Search [原文见:http://www.topcoder.com/tc?module=Static&d1=tuto ...
- leetcode面试准备:Lowest Common Ancestor of a Binary Search Tree & Binary Tree
leetcode面试准备:Lowest Common Ancestor of a Binary Search Tree & Binary Tree 1 题目 Binary Search Tre ...
- 九章算法系列(#2 Binary Search)-课堂笔记
前言 先说一些题外的东西吧.受到春跃大神的影响和启发,推荐了这个算法公开课给我,晚上睡觉前点开一看发现课还有两天要开始,本着要好好系统地学习一下算法,于是就爬起来拉上两个小伙伴组团报名了.今天听了第一 ...
随机推荐
- div布局
Margin: Margin属性用于设置两个元素之间的距离. Padding: Padding属性用于设置一个元素的边框与其内容的距离. Clear: 使用Float属性设置一行有多个DIV后(多列) ...
- [转载]Badboy使用教程
Badboy-系列教程-资料整理: Badboy自动化测试工具1 界面介绍 http://leafwf.blog.51cto.com/872759/1107079 Badboy自动化测试工具2 录制 ...
- 例子:使用C++中的this
在C++中很多的东西都传值的,. C++中的对象之间的copy是传值的 , 他不想java那样,对象之间传递的引用 , 或者说是java对指针进行了封装 , 禁止了一些不安全的操作 对于C++而言 , ...
- 学习manacher(最长公共回文串算法)
给定一个字符串求出其中最长个公共回文串. 举列子: abab -->回文串长度为2 以前的算法诸如: 扩展kmp求法过于麻烦,看到有一篇博文(http://leetcode.com/2011 ...
- Merge k Sorted Lists [LeetCode]
Merge k sorted linked lists and return it as one sorted list. Analyze and describe its complexity. S ...
- java使用dom4j解析xml文件
关于xml的知识,及作用什么的就不说了,直接解释如何使用dom4j解析.假如有如下xml: dom4j解析xml其实很简单,只要你有点java基础,知道xml文件.结合下面的xml文件和java代码, ...
- tomcat 下虚拟机部署导致应用filter失效的问题
遇到一个困扰我两天的问题 服务器的tomcat上面挂了3个项目 1个项目在D:\tomcat\\webapps\ROOT下 运行正常 1个项目在D:\heyefan下 运行正常 1个项目在D:\MA ...
- mysql 无法远程访问(授权也没办法,确切的说是因为只绑定了127IP)
默认状况下,出于安全考虑,mysql数据库屏蔽了远程访问功能. 然而在许多状况下,你需要在家或者从web程序去访问远端数据库服务器,这就相当麻烦了. 第一步: 激活网络设置你需要编辑mysql配置文件 ...
- hadoop工作流引擎之azkaban [转]
介绍 Azkaban是twitter出的一个任务调度系统,操作比Oozie要简单很多而且非常直观,提供的功能比较简单.Azkaban以Flow为执行单元进行定时调度,Flow就是预定义好的由一个或多个 ...
- JavaScript基本类型值与引用类型值
前言 JS变量可以用来保存两种类型的值:基本类型值和引用类型值.基本类型的值源自一下5种基本数据类型:Underfined.Null.Boolean.Number和String. 基本类型值和引用类型 ...