负暄琐话

我的email: rot47('649@ 6(hF+`hd"w=92vhG{>}G3"@l M >:>6?4@56 \F')

囧囧ID:g9yuayon
868563次访问,排名32好友26人,关注者30
姓名:g9yuayon
前世:夜郎国厚脸皮神棍
魅力指数:0
名气:1
宠物:一只从来不对生人叫的看门狗
g9yuayon的文章
原创 244 篇
翻译 4 篇
转载 48 篇
评论 862 篇
g9的公告
最近评论
gaoning87:if(坨="堆")
{
return 1;
}
RonoTian:不好意思,也是O(n)的。。。
RonoTian:“第二道算法题是一坨任意整数数组。写一个函数,把数组里的奇数放前面。偶数放后面。比如[1, 2, 3, 4, 5],处理后得到[1, 3, 5, 2, 4]。这次我学乖了,先演算了几个例子,然后问了他顺序重要不。他说不重要。我说,俺决定从最简单的开始,试一试顺序做,放一坨下标,指向数组起始元素。说到这里,算法出来了。然后分析复杂度,时间O(n),空间O(1)”

这坨好像……
turingbook:文字真强啊。成语一砣一砣的……
mmcjr:已阅
文章分类
收藏
    相册
    旅游
    计算机科学
    Lambda the Ultimate
    软件开发
    Reddit编程专栏(RSS)
    正在读的书
    存档
    订阅我的博客
    XML聚合  FeedSky

    转载 号外,号外 -几乎所有的binary search和mergesort都有错收藏

    新一篇: animators vs animations | 旧一篇: 加班就是好!

    是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说,他从中学到的教训是谦卑:哪怕一个简单的程序都很难写对,而整个社会却运行在庞大而复杂的代码上面。

    最后的总结很有意思:我们程序员需要各种帮助,别无它法。仔细设计很好。测试很好。形式化方法很好(不过我还是觉得有教授研究用形式化电子商务需求(比如用范畴论),纯粹无事找事)。代码评审很好,静态分析很好。但他们并不能帮我们彻底消除代码错误--他们将永远存在。我们半个世纪以来竭尽全力都不能消除一个程序错误。我们必须小心翼翼,防御性地编程,并且保持警醒。    
                                                                             

     

    发表于 @ 2006年06月04日 09:11:00|评论(loading...)|编辑

    新一篇: animators vs animations | 旧一篇: 加班就是好!

    评论

    #joyfire 发表于2006-07-11 23:49:00  IP: 159.226.42.*
    TrackBack来自《》

    细节
    #clamphammer 发表于2006-08-23 12:47:00  IP: 221.12.60.*
    写的真不错,警醒!
    发表评论  


    当前用户设置只有注册用户才能发表评论。如果你没有登录,请点击登录
    Csdn Blog version 3.1a
    Copyright © g9