CS巴别塔(1)

New Feature Fail

Posted in FAIL by Kenny Yuan on 2009/05/22
New Feature Fail

New Feature Fail

Believe it or not, but the Mac OS X trash can did not have the ability to send files back to where they came from; you had to manually copy/paste/drag them back to their original location. In the new Snow Leopard builds, a feature called “Put back” has been added, which will move any selected files from the trash back to where they came from. Users who remember Mac OS 9 and earlier will know this features as “Put away”.

Installer Fail

Posted in FAIL by Kenny Yuan on 2009/05/22
Installer Fail

Installer Fail

关于不了解的领域,兼谈Critical Thinking

Posted in Uncategorized by Kenny Yuan on 2009/05/20

Rev 0.4  (Sept. 18, 2010)

思维的特点和缺陷

人类总是喜欢歌颂自己的大脑,比如:思想的威力,逻辑的威力,数学的威力(数学来自于思考),科学的威力(科学来源于思考),还有“意识”这个“人类与动物最大的区别”[i]……blah blah……不过,几乎没有人关心过这种想法究竟来自于人体的哪个器官。

心理学中有一个研究偏见的试验是这样设计的,向随机抽样的一群人发放问卷,询问他们两个问题:1、你身边有偏见的人占多大比例;2、你自己有没有偏见,是否严重。结果,远超半数的人回答得非常一致:1、身边绝大多数人有严重偏见;2、我自己没有任何偏见。

人类的大脑就是如此:一生都在不停地犯各式各样的错误,同时还对本身保持一个较高水平的评价。之所以犯错误,是由人类的大脑运作方式决定的,这几乎是无法避免的。比如人类有一种本能化思维方式:类比[ii]——也就是说,习惯于将自己先前总结出来的规律,施加在未知但看起来相似的事物上,而不事先考虑同样的规律是不是起作用,同样,大脑也不去辨别这两种情形是不是真的“相似”。这其中的“道理”大约是这样的:“卷心菜的味道不错,圆生菜和卷心菜长得差不多,那么圆生菜的味道应该也差不多?”举个程序员们喜闻乐见的例子:你在emacs里按Ctrl-X,C按得挺熟练觉得很爽,于是到了gedit和notepad里八成也会试试看灵光与否,或者到win32 cmd下敲find -exec/xargs,或者到LISP里写visitor,或者到Python里写GetSubString(index1, index2, step)函数……在这方面,成年人和婴儿的区别仅在于知识库中积累的数据不一样多而已(所以成年人才不再去尝试已消化过的食物的味道[iii])。在用类比去探索时,我们不认为这有什么不对,但是在已经探索过的人群看来,这非常可笑并且没有必要。

试错法也是人类重要的思维方式[iv]。其中的“道理”大约是:“我不知道是不是行得通,但是做错了代价也不大,所以我想试试看”。低幼儿童会用剪刀去剪房间里的所有物体,其实也是一种试错。前面举的emacs的例子,也可以说是在试错。面对重要的东西,我们往往不会去试错[v],但问题在于每个人对于“重要与否”的评估存在着差异,导致对待同一个事物,某人觉得不重要,可以试错;但另外一个人就会对这种试错非常反感,甚至愤怒。

通常来说,在面对未知事物的时候,我们的第一印象往往是错误的,但我们会(自以为是地)认为自己是正确的。随着对事物的更多了解,每一时刻我们都会(不自觉地)修正自己对事物的看法,但每一次我们都会认为自己完全了解该事物了,认为自己达到终极正确了,同时也会忽略自己刚刚还在犯错误这一事实。这其中的“道理”大约是这样的:“我已经修正了足够多的错误了,那么这里面就不会再有大问题了吧?”在软件开发中,程序员面对BUG也经常如此。心理学中的名词“面试者错觉”(interview illusion)就是在总结这样的一种现象,因为在企业面试的时候,这种现象最显著。

归因错误(更确切地:fundamental attribution error)也是人类易犯的思维错误。其典型特征是:将好的结果归功于自己,将坏的结果归罪于别人。比如:约会迟到,那是因为堵车太厉害;如果准时到达,那是因为自己是一个讲信誉的人,故而“能够有计划地处理一切意外情况从而做到守时”。类似地还有:如果自己不守交通法(比如在“白实线”并线),那是因为有急事需要赶路;如果看到别人同样地行为,则是认为对方是一个“没有素质的人”。再回来头说软件公司的例子:处在组织中任何位置的人,包括CEO、VP、PM、Lead、QA & developer,也都会犯这种错误:一旦出了问题,大官儿会将问题推给小官儿,小官儿推给程序员,程序员推给外部条件,比如语言有缺陷,OS不稳定,SDK出BUG,Partner还延期……等等等等。如果最后成功了,程序员则会说“其实都是我们干的活儿”,CEO会说“那是因为我领导有方”,中层managers会说“CEO什么实际事情都不管,程序员们全都太懒,都是(因为)我们这些最难做人的中层经理们在不停地push”……

人类的趋利避害本能,导致思维模式中不可避免地会有将“必要条件”当“充分条件”的现象,比如放置很久的食物并不一定都腐败变质了,但我们都会选择扔掉。更进一步地,我们会将既不充分也不必要的条件也当作充分条件。比如我们在黑夜里会异常小心,虽然“黑夜”与“犯罪”既不充分也不必要;或者,看到别人手中拿着刀子,首先想到的是“不要伤害到我”。其实动物们也何尝不是如此。如果羚羊会说话,它大概会这样告诉你:“这里闻起来有狮子的味道,我不确定这里是不是有狮子,但是喝过水之后赶紧跑掉总没有坏处。”为什么不会有严格按逻辑推理行动的羚羊呢?比如一只这样思考的羚羊:“这里闻起来有狮子的味道,但这有可能是已经走开的狮子留下的;‘有狮子的味道’仅仅是‘有狮子’的必要条件,并不充分(甚至可能不必要),如果看到狮子了(充分条件),那我就会逃跑,否则我就待在河边喝水……”由于进化方向的随机性,究竟有没有这样的羚羊我们无法判断[vi],但是我们观察到现在的世界上并没有这样的羚羊。我们可以认为:即使曾经有过这样讲逻辑的羚羊,它们也已经灭绝了,因为比起“正常”的羚羊来,这种羚羊没有生存优势。对于羚羊的正确选择是:不严格遵守逻辑,按照“未来预期最大化”和“未来风险最小化”的原则行事。这是一种合理且最优的生存策略,是一种rational choice in an uncertain world(感谢Pongba提供)。在自然选择中生存下来的我们人类,也是同样:严格的逻辑思维不可能是天生的,只能依靠后天的训练。

类似这样的例子还可以举出很多很多……所有的这些本能地、习惯成自然的思维方式(包括但不限于刚刚提及的),会导致这样一种(显而易见的)结果:我们在面对不了解的领域的时候,往往会出现认知上的偏差(以上的说理并不充分。如果有人不认同我也很正常。我也无法做到充分说理,因为我的这一份大脑无法真正说服别人的那一份大脑,除非两者在思想和认识上已经有一定的共同基础)。这些认识上的偏差会导致很多问题。将这些偏差列举出一些来,也正是我写此文的目的。

不要盲目质疑权威[vii]

可能是大家过去都被无良的“权威”欺骗了很多年吧(在某些方面还被欺骗着)。在当今的年代,质疑权威变成了一种时尚(比如屡有民科的种种质疑见诸报端)。但对于懂得如何求知的人,有一条原则还是一直没有变化的:在发表与主流相反的观点前,要多作调查和思考。不要凭自己的直觉去胡乱质疑[viii]。因为在“绝大多数情况下,你都不太可能是遇该问题的幸运第一人”。如果仅凭直觉去质疑,往往会闹出笑话。甚至伟大如爱因斯坦者,在他做出过巨大成就的物理学上,曾经靠直觉指导学术研究,高举反对量子物理的大旗,但还是被无情地击败(Aspect experiment),被后人作为了反面典型,甚至有人笑称: “如果爱因斯坦自191x年开始以钓鱼为业度过余生,对科学界也没有任何损失”。[ix]

再举几个例子。比如,下面的这个科学事实[x],它和多数(未受正确汽车安全教育的)人的直觉相违悖:

a)仅使用安全气囊而不系安全带,会增加死亡和受伤的可能性。

b)安全气囊仅在与安全带一起使用时才能最大程度地挽救生命。

c)安全气囊对儿童有致命的伤害作用。

参考资料:安全带能将乘员的头部伤害减少6成,安全带+安全气囊一起能减少8成(数据来源:NHTSA)。同时,安全气囊造成儿童死亡率上升35%(数据来源:IIHS)。

又比如:近视的遗传率为90%(标准的说法为0.9)。也就是说,一个人到底近视与否,在90%的程度上是由他的父母的基因决定的。

再比如,我们平时“尝”到的各种味道,除掉有限的五种味道之外[xi],其它的都是由鼻子“闻”到而不是舌头“尝”到的。这五种舌头可以感受的味道为:酸、甜、苦、咸、鲜(一部分的上腭也可以识别这些味道)。除此之外,其它的味道都是被鼻子“闻”到的(蒸发后的唾液携带着少量食物分子进入鼻腔从而被闻到)。吃芥末的感受会让人真实体会到这一过程。

还比如,我们直觉上认为大部分为黑色的屏幕会省电,而且传统的CRT屏幕也是这样的。但是对于新型的LCD显示器,屏幕颜色对于省电则变得基本上无关紧要,因为LCD是靠背光照明,无论屏幕颜色是黑是白,它的背光是一直全开的。如果想要追究起来,白色屏幕比黑色屏幕会更省电,因为黑色屏幕是要通过电压驱动液晶分子才能得到的,而白色屏幕是未驱动时的“本来面目”(仅对TN而言的;IPS/OLED则是黑色省电)。想要让LCD显示器省电,关闭它才是王道(手动关闭/自动超时关闭)。

对于权威,不是不能质疑,事实上,许多开创性的理论都是经过质疑权威而发明的。质疑应该通过批判性思维产生,其基础则是知识和实验。关于批判性思维在这里不讲太多,请参考托马斯·杰斐逊编写的学生誓言:我保证使用我的批评才能。我保证发展我的独立思想。我保证接受教育,从而使自己能够自作判断(卡尔·萨根:http://www.cnread.net/cnread1/kpzp/s/sagen/mgcm/025.htm)。

不要放大局部经验

“不要将局部经验(或经历)当成普遍事实”。单讲这一句话不会有太多的人反对,但具体到了某件事情上,这种思维缺陷就容易出现了。每个人都有自己特定的经历,大脑会将这些经历总结归纳成经验和规律,并且不自觉地用它当成金科玉律去衡量接触到的新事物[xii]。对于我们的大脑来说,新事物都是不那么可信的——至少不如自己的经验可信。如果新事物和自己的已有经验冲突,多半我们是要去怀疑它。在怀疑的过程中,因为有了“一票否决/证伪”这样一个逻辑利器,我们的大脑更倾向于相信自己的旧有经验。举个鲜活一点的例子:一个朋友生了小孩后,经常和她的婆婆(来自某省落后的乡村)发生冲突,而冲突的内容几乎都是在育儿方法上。比如有一次,婆婆坚持要给孩子的屁股上擦香油,说如果不这样做的话,长大之后会“屙不净屎”。我想多数人看到这里会觉得比较有趣,也不会认为婆婆的这种“经验”是正确的。但从婆婆的角度来看,这是她大半辈子攒下来的经验,是她的长辈和亲朋代代相传、奉为圭皋的经验,也是她在儿子身上“试验”成功的经验。而媳妇采用的育儿方式是新事物,那是她不愿意去相信和接受的东西。婆婆的“逻辑”大约是这样的:“你说你那方法科学,那怎么还有生出来就活不了的?你说我的方法不科学,我不还是把我儿子养活这么大了?”[xiii]

回到程序员的问题上,这种应用局部经验的情形也不少。比如:“我上次这样试验过,行不通的”,或者“我这样编译过,没问题的”。“行不通”是因为什么行不通?是方法有错还是环境在干扰[xiv]?“编译过没问题”,是表面上没问题还是真的没问题,是编译器的特性还是标准中的规定[xv]

个人经验和科学理论的冲突是不可避免的,关于如何处理这种冲突,可以按科学界通用的证据原则去处理。并且,证据原则中有如下一些证据强弱的判断式:双盲>单盲,盲法>不盲,试验>观察,随机试验>非随机试验,对照实验>无对照实验,前瞻性观察>回顾性观察,系统性观察>非系统性观察( 个案系列、个案、经验),“个案系列”>个案,专业人员观察>非专业人员观察(上面这段话是引用的,从笔记里Ctrl-Y的,出处忘了)。

不要捍卫一知半解

我们都不必要、也不可能在所有领域内都做到深入了解,像“全才”一类的称呼离现在这个社会越来越遥远了(毕竟早已不是达芬奇所处的年代了)。不管你了解多少个领域,在一个领域上了解得有多深入,都不可能覆盖到所有的知识领域。所以,出现对某个领域一知半解的情况,是一个很正常的事情。比如程序员们都懂一点颜色的表示法——如何用RGB值组成颜色(做过视频处理的人估计还懂ycrcb)。但是如果用Chromatology的高度去要求,估计没有几个人能够合格了(比如D65光源的意义……)。

或者用生活中的例子:一般人都会使用相机,在不少论坛里还能碰上许多“摄影爱好者”或者职业摄影师。但你要是搬出《大不列颠摄影教程》跟他探讨镜头的像场性质(场曲、边缘解像等)、慧差、球差、色差(两种)等性质,那么多数人都会变哑巴[xvi]

或者另一个例子:几年前,在LCD显示器刚刚开始普及时,很多人都在谈论面板的反应时间,厂商也在想方设法提高该指标,但是有多少人知道反应时间是如何定义的?真正的响应是不是按标注的反应时间?(http://www.tomshardware.com/cn/953,review-953-5.html

再举一个例子,在中国有不少人会开车,在有些国家多数人都会开车。但是如果你去讨论“主减速比”、“传动比”和“刹车点”、“走线”、“换档时机”,估计多数人会感到难以理解,只有少数竞速爱好者会喜欢这样的话题。但他们也只是比普通人多知道了那么一点儿点儿,比如多数竞速爱好者对于自动变速箱中的“锁止离合器”都没有任何概念。(再比如:铝合金副车架的晶间腐蚀)

多数人对自己存在的不足,在内心深处是能够感觉到的。如果自己能够坦然面对,一般也不会有人嘲笑你(以下情况暂且不计:1、懂得太少又想冒充高手;2、遇到有人格缺陷的高手)。但是这里面有一个“面子”问题,一个人“丢了面子”就想要“找回来”。所以才会发生这样的事情:如果一个人感觉到自己在知识上被打败了,那么他就会设法从其它方面“找回面子”(往往是用诡辩)。比如,如果你和一个纯C语言的“高手”讨论OO,那么他多半会用OO的缺陷和误用来反驳,或者用OO语言中的缺陷来反驳(如执行速度太慢,或语言太复杂,以致于“为了避免复杂性也要用C”,嘻嘻),以此方式来掩饰他在OO上的不足。或者当你去和一个汇编高手讨论LISP语言的威力,那么他多半会用执行速度来避开讨论的中心,或者用“本质上来讲都是图灵机(或者本质上都不是图灵机——因为内存不是无限)”来试图抹杀差异。

(多说一句:“从本质上来说都是……”这种red herring非常好用,诱惑性极强,一个想要理性辩论的人需要努力抵制这种诱惑,like what Gabriel Gray(Syler) did。我曾见过一个用“本质论”的高手,唉……)

一知半解不可怕,可怕的是“捍卫一知半解”(特别地:当一个人在一个小圈子里是“话语领袖”的时候)。作为理性的求知者,要平稳自己的心态,摆正自己的位置,别去计较一时言语上的得失,或者说:“不要太小心眼儿”

不要扩展专家地位

学问讲究深入,市场讲究niche,所以才有刚刚说的“不要捍卫一知半解”。同样的,这一条要说的也是类似的观点,只不过是从另外的一个角度去谈。

在某一领域是专家,换了领域之后多数情况就是白丁,在这种情况下,千万不要把自己还视作专家。例如:北京师范大学教授刘里远,先后就读于儿科专业、生理专业、运动生理专业、生理专业。曾做过三年临床内科医生。这样的“专家”,在周老虎案件中的“专家地位”,就完全可以无视。

人云亦云不是事实

有强烈好奇心的人是少数,几乎没有好奇心的人也是少数。多数人对于自己了不解的领域还是有一部分求知欲望的。即使少数没什么求知欲望的,也会被各种宣传手段强行灌输某些方面的“知识”。对不了解的领域,这些“知识”的积累可能来自于口口相传,可能来自于学校和培训,可能来自于广播电视报纸网站等各种媒体,甚至可能来自于传销、推销人员的灌输。事实上,经过这些传播途径,有许多错误的说法广为流传,正所谓“谎言重复一千遍就是真理”(http://en.wikipedia.org/wiki/Joseph_Goebbels)。我想每个敬业的人,对于自己的专业都会有较深程度的了解,但是毕竟社会分工太过细化,大部分人的知识仅限于自己了解的领域,对不了解的领域,往往不会随时以严谨地态度去审视,而是很容易地就去接受。一不小心,我们就会“中招”,成为错误说法的信徒和奴隶,并将其发扬和传播到更多的人。

比如“微波炉会破坏掉80%的营养”的说法。如果我们了解一点点关于人体的知识,明白人究竟需要什么样的营养,或者说什么才叫“营养”,这种伪科学的说法就不攻自破了[xvii][xviii]。(人类所需的营养,主要是醣,蛋白,脂肪,这三者的比例相加接近100%。剩下的一点点比例是少量的维生素和微量的矿物质)

再比如“肯德鸡没有营养”的说法,也是常见的一种以讹传讹。即使不按照前面的分析法,用常理去推断也能搞明白——肯德鸡的食物是由以下的原料制成的:小麦、牛肉、鸡肉、蔬菜、土豆、玉米、青豆、鸡蛋黄、色拉油、黄瓜、腌制蔬菜、胡萝卜、奶油、巧克力、可乐(主要是水,还有少量的糖)、固体橙汁和咖啡饮料等。如果有人说馒头夹鸡腿“没营养”,我想肯定有人会想“用大嘴巴抽丫儿的”。但如果说汉堡“没营养”,为什么许多人就相信了呢?

这类的例子还有很多,比如国内对“亚健康”的概念的炒作,还有国内多年来对狂犬病的恶意误导(http://www.who.int/mediacentre/factsheets/fs099/en/index.html)……

回到编程的方面,也有不少的例子。比如:熟悉stack的人不了解continuation,熟悉heap的人不了解GC,熟悉过程式编程的人不了解declarative,熟悉OO的人不了解函数式编程,熟悉CreateThread的人不了解pthread_create,熟悉visitor的人不了解multimethod,熟悉observer的不了解:after,熟悉ON_PAINT的人不了解signal/slot,熟悉QBoxLayout的人不了解wxBoxSizer……不了解该领域的人所发表的“高论”,对于熟悉的人来说,往往会觉得很傻很天真。如果缺乏涵养,难免会冷嘲热讽,导致对方敌意地反唇相讥,最终发展成一场面子战争。

那么,如何区分某种广为流传的说法是不是真实可信的?其实很简单,还是用批判性的思维去审核,多查资料,多做功课。“不寻常的见解需要不寻常的证据”(e.g. 毒品成瘾和越战士兵的研究)。如果一个(骇人听闻的)说法在流传,要先去检验一下它有没有证据。如果没有证据,姑且当笑话听好了。如果有证据,要求证一下是不是可重复的(被其它机构独立做出相同的实验结果),再去查阅相关的批判,从中了解到更多情况,多思考,最后再得出自己的结论。(如果自己的结论与主流学术界背道而驰,那么要多加小心,因为主流学术界犯错误的概率,远小于我们作为一个门外汉而犯错误的概率)

P.S. 相比一般的科学实验,计算机“科学”方面的实验做起来就容易多了,所以我们其实可以自己动手检验广为流传的许许多多的观点(比如JAVA VM的效率之类的。http://shootout.alioth.debian.org/)

想象猜测不是事实

对于不了解的领域,我们的大脑也不会停止想象和猜测,想象和猜测不是坏事,许多发明就是这么来的。只不过如果仅仅想象和猜测,但不做功课,那就是臆测了。常见的形式有:“人家专业XX师都是……样子的”或者“这所以做出XXXX事情,肯定是因为……”,再或者“之所以数据显示是这样子,是因为……”[xix]。这样的判断,离开了数据的支持,只能说是一句空话,没有任何意义[xx]

比如在TopLanguage讨论组(http://groups.google.com/group/pongba/),前几天就发生了某些人对摄影师和闯红灯者的判断失误。在这里并不是要指责曾经判断失误的人,只是用来说明:人的大脑一不小心就会犯错(我的大脑也是一样。所以我才去不停地求证,一切靠统计数据——可信的数据)。

又比如:关于“将头伸出车窗,在某些情况下会导致受伤”这个判断,大家基本上会认同。但究竟是什么原因导致受伤?此时的想象和臆测就不管用了,需要数字来说话。但作为人类,我们当然不可能选择随机双盲法去做试验(同样,也不可能用大量婴儿去做“三鹿氰胺”的毒理/病理实验),所以只能看统计数字并加以归纳。虽然我们通常会认为:将头伸出车窗后,被其它车辆撞击/被树枝广告牌刮伤的可能性是最大的,但是对常这样做的宠物狗的统计结果表明,因飞行物和飞溅物撞击的受伤比例其实最高。同时,电动车窗受伤的报告数字也在警示人们,这种看似不起眼的危险其实发生的比率也很高(不仅仅儿童,许多成年人也是电动窗的受害者)。

再举一个例子(这个例子离多数人的生活有点远,但它正好是我的兴趣爱好。在这种情况下拿出来用于佐证应该也算是合适的):有一种格斗比赛叫做综合格斗(MMA),这种格斗的规则相当开放(它继承自古罗马斗兽场的精神):允许脚踢、膝撞、拳打、肘击,允许各种摔法,允许使用各种关节技(中国俗称擒拿),允许使用各种绞技(勒脖子令对方窒息),并且,即使一方已被打倒在地也不会中断比赛,而是允许另一方的选手用脚踩、足球踢(将对方脑袋当球踢)、或者骑在身上用拳打头或者用膝撞头[xxi](不要与WWE混淆,WWE仅仅是有剧本的表演)……单单听我描述,可能你会认为这种比赛相当残忍,而且伤亡率会相当地高。但是根据统计数字,伤亡率上来讲,看似残忍的综合格斗远比足球、橄榄球等运动要安全。

如果想要避免(或修正)错误的想象和猜测,有一个好办法:批判性地思考,同时一定要多看实验数字[xxii]

不要预设他人的未知

一个人可能不了解某个领域,但这不代表其他人也不了解。或者,一个人仅仅对某个领域有浅层次的认识,但这并不代表别人也是一样(别人可能在此领域浸淫多年)。所以,不要将自己的未知等同于别人的未知,更不要因此就(甚至故意地)说问题是“不可知”的。举个典型例子:堆积大量的术语和名词,用来说明“这个不是你我能够明白的”,甚至说“不是xx研究能够解决的”……

到底应该如何去做?

简短总结一下:对不了解的领域,要多思考再发言。思考形式应该是critical thinking,要基于对知识的掌握,基于实验和数字。不要自以为是,不要凭空思考,不要随便下结论。

P.S. 讲起道理来谁都会讲,做起来却很难不犯错。所以我们要随时自醒。引用一句话:魔鬼在细节中。

附:不成立的证明

必要条件不可证明

这个道理很明白,但中国人却容易在这方面犯糊涂,导致这方面大大小小的例子很多:

1)钢产量高是工业发达的标致,所以我们必须要大炼钢铁,赶超英美。

2)奥林匹克奖牌多是体育发达的标志,所以我们必须要多拿奖牌,赶超俄美。

3)通过了ISO 9000认证表明工作流程是标准化的,所以我们必须要努力通过ISO 9000。

4)用C++/Java编写的优良软件中常常会出现模式,所以我们必须要努力使用模式。

5)CMM认证体现出……所以我们必须要……

有的时候,这种联系并不都是“必要条件”,而仅仅是相关。但即使相关度为1的,也有不必要也不充分的情况(比如相关的这两个项目都由共同的某个原因所决定。二者都是结果,互相之间无因果关系)。举个例子:有些人会用这样的“逻辑”来论证:“在长安俱乐部有年卡的律师,应该都是名律师吧……”

如果必要条件被当成充分条件来反驳,那就更是一笔糊涂帐了,虽然表面上看起来“还挺有道理“的。例如:

论坛ID_A:只有找出良好的抽象,才能做好软件。

论坛ID_B:放屁!傻B!谁告诉你只要找出抽象就能做好软件了?你雇一堆白痴程序员加一个白痴经理试试?

(关键字提示:“只要……就”和“只有……才”)

问题实验不可证明

理论需要实验来证明。但如果实验有问题,那这种理论的可信度就要受到怀疑了。

许多人可能都听说过“可乐杀精”、“中(成)药能杀病毒”,并且将这些说法广为传播。其实,“可乐杀精”和“中药杀病毒”的试验是这样做的:1、将精液置入试管,然后倒入可乐,静置几小时,然后观察到的结果表明活动精子的数量减少了。2、将病毒培养液置入试管,然后倒入中药制剂,过若干时间后观察到病毒有大批死亡。

对科学试验方法有些了解的人,看到这里就会明白这种试验是多么地靠不住。对于不了解科学实验的基本原则的人,我这里多说两句:这些试验没有进行对比,从而导致了错误的归因和解释(应该随机双盲)。试想:如果倒入的不是可乐、中药,而是白开水、食盐、吊白块、漂白粉,或者什么都不放,会有什么结果?实际上,精子的活性离体并不长久,而且需要弱碱环境(可乐为酸性),而病毒在离体后特别容易灭活(作过病毒培养的人都知道对培养基和温湿度的严格要求)。真正的原因,在没有做对比试验的时候会藏匿起来很难发现。如果有了对比实验,为了解释实验结果,就不得不去寻找真*相[xxiii],从而也就减少了犯错误的可能性(比如这样的对比试验:培养皿中未加入任何中药制剂,只是在桌上放了2小时,然后病毒也大量灭活了)。当然,如果没有对比的试验是故意设计的,那就是另外一回事了。

这方面的例子还有很多,随便找几个:

例子1:在一个汽车论坛中有人这样来证明“开空调影响刹车”:车辆静止怠速,将刹车踏板踩下,然后打开空调,发现踏板变软了,由此证明:开空调会影响刹车。

(刹车一般采用真空助力泵,长时间踩下刹车踏板,会漏气而导致“变软”。所以开不开空调,长时间踩刹车踏板都会“变软”)

例子2:有人说,可乐是酸性的,会导致骨髂中的钙质大量流失。实验设计如下:用酸液去浸泡人的骨骼标本,经过一段时间后发现骨头变软而且有很大的弹性,再进行光谱分析,发现里面的钙质已经大量减少。

(可乐是喝到消化道里的,不可能直接接触骨骼。人的胃中有大量的胃液,其主要成分是盐酸,盐酸比可乐的酸性和腐蚀性要强多了,可以直接融化钢铁)

例子3:《郑玉巧育儿经(婴儿卷)》。其中有一节“新生儿有感情吗”中提到这样的一个案例(第92页):

“医学上有这样一个例子,非常令人吃惊:一个刚刚出生的女婴,无论如何也不吃妈妈的奶,却吃其他产妇的奶。经过多次试验,仍然如此,这使医护人员大惑不解。经过仔细调查,医护人员了解到,这位新生妈妈在怀孕初期,就极力想把胎儿打掉,直到分娩前,还很不情愿接受这个孩子。没想到,孩子出生后竟拒绝吃妈妈的奶!她宁愿吃别的妈妈的奶。如果胎儿不能领会母爱,这一切又怎样解释呢?”(本例引用自XYS网友文章,未考证与原书文字是否精确一致)

(错误的归因。这种错误很可能是由于作者的预设立场而产生的)

题外话:这样的实验,对于没有批判性思维的人,具有相当大的蒙骗性。人们习惯于接受错误的解释,这也正是宗教生存的土壤。


[i] 动物试验表明人类的近亲黑猩猩,以及善于群交的矮黑猩猩都有意识

[ii] 实验表明动物也是经常运用类比法去探索

[iii] 异食癖的不计在内,中医信奉者服用“人中黄”的也暂且不算

[iv] 在中国老一辈的程序员中,用试错法去学习知识、去编程的,也是在有人在

[v] 凡事有例外,年纪稍大的可以回想一下著名的“摸着石头过河”——10亿人的试错

[vi] 同理也无法判断是不是会进化出一只“喜羊羊”和“灰太狼”来

[vii] 同样,也不要盲目相信权威。

[viii] 所谓主流,就是说不包括中国国内的学术界

[ix] 有人说我这样写是贬低爱因斯坦。其实我写的这些都是真实情况,只不过有人习惯于将伟大的人的方方面面都搞成金光闪闪的,见不得别人批评,更有甚者不了解情况就妄下结论。其实爱因斯坦对自己的每次论战失败也都是承认的,只不过他总不服输,他从直觉上认为“老头子不掷骰子”,“一种内在的声音告诉我它并不是真实的”,认为量子物理一定在某个基础的东西上错了。爱因斯坦终其半生之力,也没有战胜量子物理,在死后还被阿斯派克特实验证明是错的(“实在性”和“定域性”只能二选一。无论如何选,爱因斯坦都是错的,因为他认为世界既是定域的,又是实在的)。许多人认为“爱因斯坦对量子物理的批评促进了量子物理的发展”,我想,理智的人都会承认这一点,但这也掩盖不了如下的事实:爱因斯坦在量子物理上犯了错误,而且一错就是几十年。如果按顺序排列的话,也应该先说爱因斯坦错了,然后再说他的错误其实也是“有一定的积极作用”的。(听着咋那么像中学历史课本评价农民起义泥?)爱因斯坦的朋友,和爱因斯坦同时代的科学家保罗·埃伦费斯特可以批评爱因斯坦的顽固,当面对着爱因斯坦宣称要以爱因斯坦为耻,因为爱因斯坦表现得像“早年容忍不了相对论的那些人”,当时埃伦费斯特手中还没有任何证据据明爱因斯坦是错的,我们这些握有可信证据的后人,难道就不能批评爱因斯坦了么?

[x]之所以称其为科学事实,说明它可检验,可解释,可预测

[xi]很多年前认为是四种,所有较早的书中也说是四种,如《裸女》

[xii] 如果这种“金科玉律”已经被一个群体所广泛接受,那么它看起来就“更接近于真理”,如朝鲜,柬埔寨,1984……,还有这一篇报道:http://www.gmw.cn/content/2007-05/14/content_605779.htm

[xiii] 现代医学将发达国家的平均寿命提高到了70多岁,而在此之前,这些国家的平均寿命均没有超过40岁。其中,现代医学对婴儿存活率的提高是非常显著的。如果想要检验,可以试着去找一些50岁以上的人,问问他们的兄弟姐妹夭折过多少。或者参考以下数据:中国在2007年婴儿死亡率(IMR)为15.3人/一千人,(这相当于美国1960年的水平,美国在2002年婴儿死亡率为7.0人/一千人)。与最早有记录的数据比较一下:在1915年的美国,婴儿死亡率为99.9人/一千人;中国的婴儿死亡率在1958年为80.8/一千人,也就是说,当时有将近十二分之一的婴儿是活不下来的(在1948年,则有五分之一婴儿会死掉)

[xiv]编译器BUG,OS出错,内存刷新失败,宇宙射线……

[xv] 比如嵌套类的访问权限和friend声明

[xvi] 有些人会倾向于将“职业”看成更“懂行”、更“专业”。其实这二者没什么必然的决定关系,而且在很多行业均是如此。其实,职业只代表以此为业,能达到职业最低标准即可。懂得多少则在于个人修为。

[xvii]不是所有的非科学都是伪科学,但是如果它明明不是科学,还非要宣称自己的科学的,这样它才算成是伪科学

[xviii] 即使对食物中的微量营养进行检测,用微波炉烹调也是有益无害。比如《美国食品科学》中记录了这样的实验结果:微波炉烹饪方法比传统烹饪方法维生素C损耗减少20%,维生素B1损耗减少40%。中国食品卫生监督部门以前也作过类似的检测分析,卷心菜经微波炉烹饪,维生素C的损耗率为4.76%,而传统烹饪方法导致的损耗率却为19.04%。另外,食品中矿物质、氨基酸的存有率也比其他烹饪方法高得多。如用微波炉烹饪蹄膀,八种必需氨基酸为微波加热前的98.6%

[xix]在国内的汽车论坛,我差不多是最早推广宣传IIHS的统计报告的。当时有很多人看到我列出的数据后感到不爽(因为他们喜欢的车型的伤亡率很高,他们批判的车型的伤亡率却很低),于是他们就找出各种理由批判。在这些种理由中,没有一个站得住脚的,比如说“伤亡高是因为保有量大”,其实原始数字就是折合的百万辆车的伤亡率,或者说“某些车型的伤亡率高是因为旧款多”,其实原始数据已经标明了生产年份……IIHS是一个比较严谨的机构,早就考虑了种种情况,将数据梳理到合理才发表的,而且发表的文件中也有注明。这些人很可能是没有仔细看数据,就凭着自己的喜好开始了想象与猜测,所以才会犯下如此多的低级错误(刚才最后这一句话也是我想象的,嘻嘻)

[xx] 如果不依靠数据的话,甚至许多专业领域内浸淫多年的资深人士,对事情的判断也常常会出错

[xxi]网上某些所谓“铁笼中的黑市拳”的照片,其实就是来自于一个叫UFC的综合格斗赛事

[xxii] 要警惕玩弄数字的诡辩者

[xxiii] 我知道“真*相”二字在国内大多数可“自由发言”的地方是屏蔽的,所以要用间隔符号。

Tagged with:

科普一下算法的度量——Big O, Big Omega, Big Theta以及Udi Manber的大OO

Posted in Uncategorized by Kenny Yuan on 2009/05/20

[引子]
在很久很久以前……久以前……久以前……久以前——有一个阿拉伯的故事……伯的故事……伯的故事……伯的故事——故事的发生是这样的……是这样的……是这样的……这样的——大约是90年代初期,当时还算天真无鞋、聪明蟀气的俺,一不小心看到一本老教授们给小盆友们编的奥赛辅导,里面给出了一个无法证明但是被我无数次证伪的公式:程序=算法+数据结构。于是我立即去买了《Data Structure》,也就不小心中了绿毒(Pascal第一版),大学时又被迫买了蓝毒(ISBN7-302-02368-9)

(据说ISBN自带校验,码矩为2,哪位达人可以算一算,看我上面抄写的书号是不是有问题?严重怀疑俺们大学老师卖D版书的说)

现在终于可以开始解毒:科普一下算法的度量——Big O符号、Big Omega符号和Big Theta符号的区别,以及Udi Manber的大OO符号

[正文]
大O符号是德国人Paul Bachmann在1894年引入的,此人精通数论,研究过费马大定理。在他生活的年代还没有计算机,认为mathematics在CS中灰常重要的人可以长笑三声了(当心引来一个红脸的放屁虫)

定义啊定义
–> 正常人类请跳过,火星人驻足,金星人可以去逛街了
A、大O的定义:
如果存在正数c和N,对于所有的n>=N,有f(n)<=c*g(n),则f(n)=O(g(n))
B、Big Omega的定义
如果存在正数c和N,对于所有的n>=N,有f(n)>=c*g(n),则f(n)=Omega(g(n))
C、Big Theta的定义
如果存在正数c1,c2和N,对于所有的n>=N,有c1*g(n)<=f(n)<=c2*g(n),则f(n)=Theta(g(n))
(友情提示:大O和大Omega主要是>=和<=的区别)

简而言之
–> 正常人类观看,火星人请去multiverse求解EPR paradox(http://en.wikipedia.org/wiki/Epr_paradox),注意不是ERP,这个EPR和空洞骗钱的企业软件ERP不一样(其实EPR比ERP更加“空洞”,呵呵)

重来重来,呵呵——简而言之,广而告之:
1、O是一个算法最坏情况的度量(悲观主义者喜欢这个符号)
2、Big Omega是最好情况的度量(favorite notation of optimist)
3、Big Theta表达了一个算法的区间,不会好于某某,不会坏于某某(不这就是中庸之道嘛,偶喜欢)

不管是O,Omega,还是Theta,都隐藏了常量C的信息,因为一般来说这个常量是不重要的,当n值变得很大的时候,对时间和空间的耗用,还是由函数本身的阶来决定的。如果n很小,用什么算法都可以,所以我们一般用极大的n值来对比算法的优劣。但是在实际使用算法的时候就要注意,如果n值不大,就先去优化别处吧!

如果想看些具体的例子可以到这里:http://www.cs.mcgill.ca/~cs251/OldCourses/1997/topic3/

可能会有人极不赞同“常量C不重要”这句话,举(抬)一个例子:如果C=10^80(也就是宇宙中的粒子数量),那该怎么办?

先不说这种杠抬得是不是“欠抽”,因为一个自洽的系统欢迎任何抬杠,但是在编程时多半是不行的。别说这么大的常量复杂度了,就是用些边界条件,随手往里抛一些“单位元”或“逆元”,不少算法都会趴下。(用人类语言解说上述名词的结果为:单位元——0或1,视加法群和乘法群而定;逆元素——改变正负号。参见代数系统中群的概念http://en.wikipedia.org/wiki/Group_(mathematics) 注意wiki中的链接是有后半个括号的)

如果你也认为常量C很重要,那么恭喜你了——不光是geek和IT愤青,连Udi Manber(http://manber.com/)也是这么想的!Udi提出了一个新的符号,OO,用来表达这个常量C太大的情况。如果O(n)=1,但是未能表达出来的那个邪恶的常量C,其真实的取值是10^80,那个,这个算法还是不能被使用。

多八褂一句:Udi Manber后来去了google,还做了VP。想来也对,做算法(尤其是搜索算法)和网络研究的大牛,去google工作也算是众望所归吧。

[伊索寓言式的结论]
这故事是说,学习要先找对教材。:P

Tagged with:

一次成功的远距离调试——1亿美元的硬件,1亿英里的距离

Posted in Uncategorized by Kenny Yuan on 2009/05/20

关键词:比天空更青蓝的悠远,比精金更珍贵的飞船……(听起来像不像深蓝判决?哈)

在Practical Common LISP一书(http://gigamonkeys.com/book/)中,讲了这样的一个故事:在那并不十分遥远的1998年,在1亿英里的高空,一架造价 1亿美元的太空船(NASA Deep Space 1:  http://nmp.nasa.gov/ds1/,不过这个网站被GFW了),其控制程序出现了条件竞争的BUG,而这个BUG在地面测试中没有发现(嗯,符合经典的软件规律)。于是,负责这部分代码的人就远程连接上飞船(是真正的远程啊!),从飞船上的LISP程序中调出来REPL窗口,在系统还在运行的时候进行诊断和调试,并修正了BUG,成功地部署,最终完成了任务。

这真正是一次成功的远距离调试——1亿美元的硬件,1亿英里的距离。

Tagged with: ,

我为什么学习了编程

Posted in Uncategorized by Kenny Yuan on 2009/05/20

大约是小学3年级到4年级的时候,我从家里的大书架上翻出一本书,书名大概是《逻辑代数及其应用》,浅灰色的书皮,上面有很多类似电路的纹路,还印着好多算式,差不多就是下面这样子:

0+1=1
1+0=0
0+0=0
0*1=0
1*1=1
0*0=0
……

一看到这本书,我立刻就开始喜欢了——WK!真是太简单了!都是0和1,连2都没有哇!太好了,我就学它了!

于是,就开始慢慢陷入了“英国人不说谎话”和“法国人才买机票”的一类的问题中……

P.S.上初中的时候学习了Prolog,目的就是从不再做人肉解题机。可惜现在几乎全忘了

再P.S.推荐一个视频:用多米诺骨牌来解释逻辑门 (http://www.youtube.com/watch?v=SudixyugiX4)

Tagged with:

早早学习脑瓜好——关于认知发展的读书笔记

Posted in Uncategorized by Kenny Yuan on 2009/05/20

人们一般认为婴儿的大脑还未发育完全,需要在成长过程中慢慢发育。传统的认知科学也认为神经的发展止步于童年(如皮亚杰)。但事实正好与人们的想像相反:新生儿大脑中的神经元数量与成人基本相同。而胎儿的神经元数量远远超过成人:28周的胎儿的神经元最多(可达成人水平的数十倍),然后慢慢减少,在出生时稳定在与成年人大致相同的水平上。用来联结神经元的神经突触也是婴儿最多:新生儿大脑的神经突触在2岁的时候达到峰值,到16岁的时候就只剩50%了。在人的一生中,神经突触都在不断地减少。
当儿童成长到青春期的时候,神经突触要经历一次大幅度的删减(称为”选择性消除”),同时强化已有的(叫做”功能确认”)。虽然人的一生什么时候都可以学习新的知识,但是相比起来,在小的时候没有好好启蒙的人,学习起来会非常吃力。

有一种理论认为:如果婴儿/儿童在成长过程中的环境单一,接受的刺激太少,神经突触就会更多地被消除。反之,就可以更多地保留神经突触。

这一理论在动物实验中也得到支持:家养的动物的大脑要比同种野生环境下的小10-20%;生活在复杂环境下的小鼠,与生活在简单环境下的同种小鼠相比,其神经元具有数倍的分叉(树突)。

对双生子的研究也表明,当儿童成长到16岁时,遗传影响在语言方面仅占50%,在空间能力方面仅占40%,其它的均为后天环境的影响。

所以,对儿童来说,不停地让大脑接受各种刺激,比如:学习迷语,观看电影电视,听音乐,听广播,触摸,学习外语,体育活动、接触乐器、下象棋,学习科学知识,学习数学,阅读文学作品,写作,构造简单的建筑或其它物品(创造性的),逻辑训练(语言逻辑和数理逻辑)……这些都能够强化已有的神经突触,让它们幸免于被消除。在今后的一生中随时发挥作用。

P.S. 本文为读书笔记,故未能充分说理,也未列出参考资料的来源。

Tagged with:

读弱智故事,享忽悠人生——如何防忽悠?

Posted in Uncategorized by Kenny Yuan on 2009/05/07

卷首语:21世纪什么最重要?防忽悠!

1 关于电脑的故事,忽悠程度不高

先看看这个故事,体会一下被忽悠的感觉(在这里我假定你对电脑故障比较熟悉)。

《电脑对黄碟过敏么?》

某电脑公司收到投诉信,用户抱怨说他每晚都要看碟。如果看的是黄碟,则他新买的奔腾二代电脑会自动重启;如果看其它的碟,则电脑可以正常观看。天天如此。该用户怀疑这电脑是否对裸体过敏。
电脑公司的头头觉得这太过诡异,不过还是派了一个工程师去该用户家调查原因。第一天,工程师和用户一起去看碟。在家里,工程师要求看黄碟,结果放出来后, 电脑果然重启了。此后几天,工程师每次都和用户一起看碟,每次都由工程师临时决定看什么碟。果不其然,凡是看了黄碟,电脑就会重启;反之则正常。(由于是工程师临时决定看的类型,可以排除用户搞恶作剧的可能)
这个工程师是一个理性的人,也不信神,当然不会相信电脑对黄碟过敏这一说。但是他觉得有更深层的原因在起作用。此后,他每天晚上和该用户一起去看碟,每次 他都详细记录开机的时间、途中点鼠标左键和右键的次数、使用的软件版本等各种信息。许多天后,他终于发现了规律:黄碟一般都比较破旧,而且碟片上充满了划 痕。
于是问题就转化为:读破烂的光碟导致电脑重启。然后,工程师就轻易找到了原因(当读破烂的影碟的时候,光驱功率过大,导致电脑电源功率超载,造成供电中断,电脑重启)。

这个故事是在说,透过现象看本质,要胆大心细,才能做好工程师……blah blah

(被忽悠了的请去面壁一分钟)

为什么说这个故事是忽悠?因为在奔腾二的时代(如1999年),光驱功率过大造成电脑重启是一件很常见的事情,对于工程师来说,不需要那么多时间去反复看 不同的碟,以及记录鼠标/键盘动作等。对于无蓝屏的重启,第一怀疑就是供电,接上示波器看看波形便知。在实际维修中都不用这么麻烦,因为稍微有点经验的 人,听到光驱狂转的巨大声音,都能联想到光驱的功率太大了(知道这事儿的人挺多的:在我上大学时,前后三栋宿舍楼的人基本上都知道这回事儿。其实,只要常 看碟的人,基本都知道这回事儿。《电脑报》等报刊上也登过)。对于专修电脑的维修师傅们,一听说“看碟时电脑重启”,第一反应也差不多应该是“光驱功率过 大,电源吃不住了”。经历过PII电脑时代的人对这个现象简直是太熟悉了(与现在不同,那时候电源是不被重视的,多数的质量很差)。在那个时代,多数人都 特别关心影碟的质量,租碟的时候都要先看划痕多不多,以便评估一下是不是好读(因为看碟时读不出,导致电脑死机是常有的事)。如果有人抱怨碟片读不出、看 不了,别人说的第一句话基本都是“碟太烂了吧”……所以,对这种现象,一个正常的工程师根本用不到花费“许多天后”才得到正确答案。

2 关于汽车的故事,忽悠程度就高多了

二次忽悠来到,这次是关于汽车的(在这里我假定你对汽车技术和修理程序不熟悉)。

《Pontiac汽车对冰淇淋过敏?》

某汽车公司收到投诉信,用户抱怨说他每晚都从家里开车去商店买冰淇淋。如果买的是香草冰淇淋,则回家时汽车就无法发动;如果买其它口味的冰淇淋,则汽车可以正常发动。天天如此。该用户怀疑这汽车是否对香草冰淇淋过敏。
汽 车公司的头头觉得这太过诡异,不过还是派了一个工程师去该用户家调查原因。第一天,工程师和用户一起去买冰淇淋。在店里,工程师要求买香草口味,结果出来 后,汽车果然不能发动。此后几天,工程师每次都和用户一起去买,每次都由工程师临时决定买什么口味。果不其然,凡是买了香草口味,汽车就无法发动;反之则 可以。(由于是工程师临时决定购买的类型,可以排除用户搞恶作剧的可能)
这个工程师是一个理性的人,也不信神,当然不会相信汽车过敏这一说。但是他觉得有更深层的原因在起作用。此后,他每天晚上和该用户一起去买冰淇淋,每次他都详细记录往返的时间、途中踩油门和刹车次数、使用的汽油型号等各种信息。许多天后,他终于发现规律:凡是买香草口味的,在商店里面花的时间少(因为这个口味受欢迎,摆放的货架靠门口)。
于是问题就转化为:停车的时间短导致汽车不能正常发动。然后,工程师就轻易找到了原因(当停车时间太短,发动机依然很热而无法驱散气阻)。

这个故事的启发是……布拉布拉……

(译文抄自http://blog.csdn.net/program_think/archive/2009/02/02/3858867.aspx,对BLOG作者无恶意,切莫误解)

你被忽悠到了没有?

2.1 一大段关于发动机的技术解释

为 什么说这个故事是忽悠?先得说说什么是气阻(气锁):对于发动机来说,气阻这个词是说进气道内的空气阻力(说得不严谨,但这样说比较好理解)。气阻过大时 会影响混合气进入燃烧室,这时就形成了气锁(也不严谨,其实不只是在进气道内会有气锁,曲轴箱也会有)。也有把气锁称为气阻的,但Vapor Lock这个词这样翻译有点不合适,没体现出“Lock”一词的意思来。

不同温度下空气的体积是不同的,特别对于进气道这个温度变化范围很大的地方。正常的发动机温度是90度,但那是指发动机水套内的平均温度。气缸内的燃烧温度有1000多度,排气温度也有几百度。相比起来,经过滤清器的新鲜空气温度都很低,顶多也就是40度的样子。所以很多车都将排气歧管和进气歧管设计在一起,形成一个热交换器(也有用水套的水进行加热的,在小循环温度足够时就可以加热了),用排气歧内的废气来加热混合气,混合器的温度上升,有利于气油的雾化,但同时也会造成空气的膨胀,导致氧气减少,造成不完全燃烧,甚至无法燃烧。说到进气歧管,附带说一下可变进气歧管。进气歧管越长,低速扭矩越大(因为气体分子有粘滞性),但是在高速时,长长的进气歧管却很难及时供给混合气(还是因为气体分子的粘滞性),这时要求有较短的进气歧管。所以,现代发动机中才会有可变进气歧管的设计。为什么要说到进气歧管的问题呢?因为进气歧管的问题也是气阻造成的。(注意这里没有说可变正时/升程的进气门/排气门,俺比较讨厌本田作秀的VTEC)

以上一段说的不适用于直喷机,因为直喷机没有可燃混合气。另外,所有的Diesel都是直喷机(MM们请注意,这个不是时装品牌那个Diesel……)

气阻(气锁)不一定是在进气道里形成的,油箱也会造成气阻。油箱基本是密闭的。油箱设计有通气道,在两种情况下会打开,一是大量汽油已被送向发动机,油箱内的压力减小,导致一个负压,进而影响低压油路内的气油流动,如果负压过大,就会造成气锁,使低压油路内的气油无法流动。所以,在油箱内压力小于一定数值时通气孔打开放入空气,补充气压。第二种情况是汽车受热,汽油在油箱内挥发,产生大量油蒸气,造成油箱内压力过大,此时要打开通气孔将汽油蒸气排入空气中。(所以,不要将你的车放在太阳下暴晒,气油会蒸发,最终跑光光的~~)

为什么高温启动会碰到气阻/气锁问题呢?理由简单得出奇,因为发动机本来是为了低温启动设计的。在极低的温度下,发动机想要启动,是非常困难的,因为汽油蒸气在低温下很难形成,影响燃烧;而且低温下润滑油粘性很高,各部件间的阻力增大。所以在现代发动机的进气道内都设有陶瓷电阻用来预热混合气。这种电阻很有意思,在低温下它有一定阻值,会发热,但温度升高到几百度的时候其电阻值会变成无穷大,此时电路断路,不再加热进气道内的混合气。

既然发动机原本设计成”偏向于低温启动”,那在高温的时候出现气阻影响启动也就是有可能发生的事情了(参数不匹配的问题或其它意外情况,另:正在工作中的发动机遇到气锁也会熄火)。所以,现代发动机的设计和测试都是对“低温启动”和“高温启动”这二者兼顾,一个也不能少。实际上,气阻是发动机设计的关键中的关键。发动机设计的至高境界是:让空气顺畅地进入燃烧室,与汽油一起进行最大限度地快乐燃烧,然后再通畅无阻力地排走燃烧过的废气(各种工况下均应如此)。这样,就能最大限度地压榨功率(e.g. 升功率100ps+)。常用的手段有:

  • 加大进气量
  • 加大喷油量(前两项加起来也就是提高压缩比了)
  • 喷射燃油由电子系统控制
  • 缸内直喷(活塞吸进来的只是空气,燃油直接喷射到汽缸内)
  • 不均匀地喷油(它有一个名头响亮无比的缩写:FSI)
  • 减小进气阻力和增大进气量(如:正时/升程可变,可变进气歧管)
  • 减小排气阻力(如:正时/升程可变以减少排气门漏气,拆掉催化器的直排)
  • 提高进气的压力(SUPERCHARGE或TURBO)……

(请数一数在上面列举的条目中有多少次涉及了气阻问题,呵呵)

2.2 为什么我认为这个故事也是忽悠

说了这么多,你想到什么了没有?——气阻是这么基本、这么重要的东西,连我一介票友都能大喷特喷出这么多来,难道发动机的设计者竟然犯下了如何严重的错误?虽然Pontiac从来没出过让人称赞的好车,但它也是很普及的车型(在美国的租车行里很常见),会有这么严重的设计失误么?这个问题我没有答案,TX们可以自行判断。

在我看来,如果是因为气阻致使发动机无法在热机工况下启动,应该属于个例,是需要维修的故障,而不是车型的通病。这样的故障在维修工那里通过检查维修check list项目就能重现。对于无法启动的故障,维修工是很熟悉的:一般要查油路,查缸压,查喷油嘴,查进气道,查节气门开度(如果是化油器的还要查阻风门等),查冷车启动,查热车启动……哪怕是在汽车维修水平极其低下的中国,4S店的小工也特别熟悉这一套东西。

如果我们假设去买香草冰淇淋的时间是1分钟,这时的气锁导致发动机无法启动。那么,在发动机熄火后10秒钟时尝试启动,也同样会遇到气锁问题(此时的发动机更热),同样无法启动。我相信任何修理工/工程师对于发动机无法启动的问题,不会仅仅跟着客户跑来跑去,在“许多天”里都从来不去尝试“冷车能否启动”和“热车能否启动”这两个必查项目的。(不管是在脑子里,还是维修检查用的check list,都会有这两项的)

我最早看到这个故事是在六年前的TOM汽车论坛。当时也有不少人怀疑这个故事的真实性。我在网络搜索了很久,也没有能够确定其真伪。但我一直认为,这个明显有漏洞的故事,应该出自《读者》那类“喜欢编故事、又无法自圆其说”的刊物。

当然,上面我说的这些不能证明这个故事肯定是假的。但是我可以肯定地说:可疑点太多了。面对众多可疑点却毫不怀疑,这就是被忽悠了。

3 如何防忽悠

一句话结尾:要了解故事的背景知识,同时具备批判性思维(Critical Thinking)

附:延伸阅读

1)Wiki Critical Thinking
http://en.wikipedia.org/wiki/Critical_thinking

2)本人的另一篇烂文:《关于不了解的领域,兼谈Cirtical Thinking》(勉强算是个通俗入门版)
http://blog.csdn.net/yuankaining/archive/2008/12/16/3529833.aspx

3)强人介绍速成经验的:《如何清晰地思考:近一年来业余阅读的关于思维方面的知识结构整理》
http://blog.csdn.net/pongba/archive/2008/12/18/3549560.aspx
(其实我说谎了,速成不了的,要花很多时间的……)

Tagged with:

浅谈OO编程中必不可少的手段:Multimethod

Posted in Uncategorized by Kenny Yuan on 2009/05/07

浅谈OO编程中必不可少的手段:Multimethod(又名Multiple Dispatch、Generic Function)

这可能是我写得最中规中矩的文章标题了。写这个文章的目的很傻很天真:“在網路上能夠google到的介紹multimethod的漢語文章很少”。有本很出名的书写了一笔(More Effective C++: Item 31),但很快就转而使用替代方案了。所以,我打算写一个这方面的文章给网络补充一下资源。算是遵循 “勿以善小而不为”的古训吧:)

一点历史介绍

在 上世纪的80年代中期,在LISP中出现了CLOS(读成see-loss)系统,它是Common Lisp Object System的缩写。CLOS被提出之后不久正好赶上了ANSI标准的制定。CLOS是如此之优秀,以至于它一字不改就被收录成了ANSI标准。因为它是 ANSI标准的一部分,所以基本上所有的Common LISP都带有CLOS。CLOS提供了很强大的功能,包括class定义,子类化(派生/继承),multimethod,before/after /around,重定义class,改变object的class,重新初始化object等功能(除此之外,CLOS还提供了一个强大的Meta- Object协议)。所有的这些,从功能上超过了任何其它支持OO编程的语言(就如同LISP的Condition超过了其它语言的Exception一 样)。

Multimethod是在设计CLOS的过程中提出来的。因为设计者们发现,不加入Multimethod的话,OO编程实践中 就缺少一个必要的手段。从这个角度来说,虽然Multimethod只能算CLOS中的一碟小菜,但它却是一碟很多人都需要、但是C++和JAVA就是不 给上的小菜(上的都是酸菜???)

——为什么要这么说呢?且慢慢听我道来……

很自然很舒服的面向对象

在多数教科书和随笔文章中,当讲解到面向对象技术时,一般都会举出动物的例子。我临时也编出来一个:
生物分为动物和植物;而动物可分为两栖、爬行和哺乳动物。哺乳动物中有狗有狼还有人,哺乳动物都有一个共同的行为:哺乳;两栖动物都有一个共同的属性:变态。

这个例子能很好的说明OO技术的自然性——它和人类认知中的concept正好对应[注1]:
1、Concept是一个分类方法;
2、Concept有它不同于其它concept的特有属性;
3、同属某个concept的个体有同样的属性;
4、Concept可以有层级关系,子concept拥有父concept的所有特性(但不一定能当成父concept来用,参考Liskov Subst Principle);

从这个角度来说,OO是成功的,因为它用很自然地,符合一般人思维的方式解决了一个复杂的归类问题。(这里暂且不提interface这回事,其实真正的OO是interface的天下,但interface却不是那么“自然”的)

如果我们记住了一个概念,我们就能够了解它所代表的事物的特点和功能,从而能够很好地使用它。OO技术带来了同样强大的(复用)能力——只要知道了class名字,就可以知道object的属性和功能,并方便地使用它们。

上面的例子可以总结成这样的关系:

类别间的关系

  • 植物 is a 生物
  • 动物 is a 生物
  • 两栖动物 is a 动物
  • 爬行动物 is a 动物
  • 哺乳动物 is a 动物
  • 狼 is a 哺乳动物
  • 狗 is a 哺乳动物
  • 人 is a 哺乳动物

行为

  • 两栖动物 can 变态
  • 哺乳动物 can 哺乳


(狼、狗、人的“哺乳”行为可以从“哺乳动物”中继承而来,不需要再进行重复声明)

分类和交互:复杂性之源

世界之所以复杂,不仅仅在于事物的多样性(分类),还在于事物之间的交互。一般意义上的交互是简单的,比如下面这段程序伪码(本想用汉编的,无奈我太笨,学不会,只好用英编了)

对于打印机打印图片,我们可以这样写
function Printer.Print(RMB rmb) Action:
1. Render rmb to memory;
2. Send memory to PrinterDriver;
3. Submit to PrinterDriver
End Action;

(似 乎最近从“瘾科技”开始,不少中文站都在说钞票防伪的事情。我也来PS一杠子:世界上有几十种钞票都是打印不了的,因为有Omron的猎户座防伪专利技 术,当然,在1996发行那种50马克之前,就不会有这个问题。具体例子和应用了此技术的钞币列表请参见:http://www.ybnotes.com /cn/ennewslist.asp?id=434)

现在我们用简单的动植物模型来实际编一些程序。基于食物链方面的常识,我们可以这样总结:
1. Animal.Eat(A Plant);
2. Animal.Eat(An Animal);

于是,我们这样定义动物类型,添加一个方法,名叫“吃”:
class Animal;
class Plant;

function Animal.Eat(Animal victim) Action:
me.Energy += victim.Energy;
End Action;

function Animal.Eat(Plant food) Action:
me.Energy += food.Energy;
End Action;

如果我们定义了猿是动物的子类别:
class Ape derived from class Animal;
那么Ape同样可以去Eat所有的Animal和Plant(child class从super class继承而来的)

你可能注意到了,对于同一个类型“动物”,我们定义了两个同样的方法,都名为“吃”,但是有不同的参数。是的,我们可以这样编程,这东西有个很响亮的名称叫重载overload。在这一点上,编程技术也是符合人们的思维习惯的。

啊——喔——出现了特例!

接下来的文章内容,非常符合作为读者的你的预料:我要开始讲特例了。一般在特例之后都是将程序搞砸,然后再引入清晰的解决方案来显示它有多么好,但是,我不想这样搞。我只想按照自然的思路来进行。不过,特例还是要引入的:)

话说那天下大事,如果真的是这样地简单那该有多好(前面一句请用单田芳的语调来念)。只是事情往往不在最完美的阶段结束,就像好莱坞电影里说的:And death… is only the beginning…

在一般情况下,上面提到的方式运作良好。但事事有例外。比如我们突然发现有种植物叫猪笼草,它竟然可以吃掉小昆虫!
1. Animal.Eat(A Plant);
2. Animal.Eat(An Animal);
3. SomePlant.Eat(An Animal); // 吃动物的植物

其实不只是猪笼草这样的交互特例。前面的例子也省略掉了许多分类方式,比如动物要分为食草、食肉、杂食的,除此之外有微生物。吃昆虫的植物其实也进行光合作用……

如果编一张表格,所有的关系就清楚了。

生物 动物 植物 食肉动物 食草动物 杂食动物 微生物 捕食的植物 猪笼草
生物
动物
植物
食肉动物
食草动物
杂食动物
微生物
捕食的植物
猪笼草

我 目前不打算完成这个表格,因为这个表格搞起来很麻烦(如果是左小龙的话他肯定也不干,除非那表格的体温是37 度:http://www.qidian.com/BookReader/1100289,21986729.aspx)。但即使不完成这个表格,我们也 能知道表格里会有许多重复项目,比如:如果食草动物能吃所有植物,那么它就能吃猪笼草(虽然现实生活中它们基本是不吃的)。有了前者作为规则,后者是不需 要再描述一遍的。一般说来,super class的规则基本上适用child class,但是child class有时候会有特殊规则。如果能够识别child class,对于特殊的child class按照特殊规则处理,没有特殊规则的就按super class的规则进行处理,这样就比较完美了。

用Multimethod编写交互规则

可 能很多人都同意这一条:变化是重构的ringing bell。考虑到植物动物和微生物的分类关系,与它们之间的“吃”与“被吃”的复杂关系。很明显地,重构的结果应该能够表达上面的这个表格中的内容。但手 工编码所有的这些对应关系肯定是last way to go(参见本文的last paragraph)。如果能有一种书写方式将一般规则和特殊规则结合起来——child class拥有特殊规则时就应用特殊规则,否则就应用针对super class书写的一般规则——如果能这样书写就同样自然、同样舒服了:

这种书写方式就是Multimethod:

一上来还是class的定义:
class Organism; // 定义生物
class Animal derived from class Organism;   // 定义动物类型
class Plant derived from class Organism;    // 定义植物类型
class Microbe derived from class Organism;  // 定义微生物类型
class Insect derived from class Animal;     // 定义动物类型
class PredaciousPlant derived from class Plant;  // 定义捕食的植物类型
class Nepenthes derived from class PredaciousPlant; // 定义猪笼草类型
class Herbivores derived from class Animal; // 定义食草动物
class Carnivore derived from class Animal;  // 定义食肉动物
class Omnivore derived from class Carnivore and class Herbivores;   // 定义杂食动物
class Ape derived from class Omnivore;      // 定义猿

然后是Multimethod方式编写的交互规则:

// 生物互吃:暂时不知道如何定义
function Eat(Organism predator, Organism victim) Action:
Print “Don’t known how to eat”;
End Action;

// 草食动物吃植物(包括杂食动物)
function Eat(Herbivores predator, Plant victim);

// 肉食动物吃动物(包括杂食动物)
function Eat(Carnivore predator, Animal victim);

// 捕食的植物号昆虫
function Eat(PredaciousPlant predator, Insect victim);

// 猪笼草吃昆虫
function Eat(Nepenthes predator, Insect victim) Action:
Print “Any special processes for nepenthes”;
End Action;

// 草食植物吃磨菇(包括杂食动物)
function Eat(Herbivores predator, Microbe victim) Action:
If victim.IsMushroomLike then …;
End Action;


// 微生物分解死去的动物
function Eat(Microbe predator, Animal victim) Action:

if victim.IsDead then call decompose(victim);
End Action;


// 微生物分解死去的植物
function Eat(Microbe predator, Plant victim) Action:
if victim.IsDead then call decompose(victim);
End Action;


很简单、很自然,很符合人类思维。

熟悉OO的可能会发现,这写法看起来很像重载,但实际上……它是一个电吹风运行时的机制。Multimethod可以按参数的实际类型决定调用哪个函数,也就是说,你可以按Organism类型传入参数,但它实际是一个吹风机猪笼草对象。在运行时,就会首先用猪笼草类型去匹配,以便决定使用哪个函数。
Multimethod与重载(overload)是有明显的区别的。重载是编译时的机制,在运行时不会起作用:如果你以Organism类型传入两个参数,不管实际上它是什么样的child class object,它只会去调用Eat(Organism, Organism),而不会去尝试区别实际child class。

在LISP中测试Multimethod

学习翠花,直接上代码。因为这些代码的自我说明能力很强,就不多加说明了。

(defclass organism() ()) ;;; 定义生物类型
(defclass animal (organism) ())   ;;; 定义动物类型
(defclass plant (organism) ())     ;;; 定义植物类型
(defclass microbe (organism) ())   ;;; 定义微生物类型
(defclass insect (animal) ())      ;;; 定义动物类型
(defclass predacious-plant (plant) ())   ;;; 定义捕食的植物类型
(defclass nepenthes (predacious-plant) ()) ;;; 定义猪笼草类型
(defclass herbivores (animal) ())  ;;; 定义食草动物
(defclass carnivore (animal) ())   ;;; 定义食肉动物
(defclass omnivore (carnivore herbivores) ())    ;;; 定义杂食动物
(defclass ape (omnivore) ())       ;;; 定义猿

;;; 声明Multimethod函数
(defgeneric eat(predator victim)
(:documentation “Something eats another”))

;;; 生物互吃:暂时不知道如何定义
(defmethod eat((predator organism) (victim organism))
(format t “Organism –> Organism: Don’t known how to eat…”))

;;; 动物吃植物:只有某些动物吃植物
(defmethod eat((predator animal) (victim plant))
(format t “Only some of animals eat plant…”))

;;; 昆虫吃植物:多数昆虫都是吃植物的
(defmethod eat((predator insect) (victim plant))
(format t “Most of the insects eat plant…”))

;;; 草食动物吃植物(包括杂食动物)
(defmethod eat((predator herbivores) (victim plant))
(format t “Herbivores –> Plant”))

;;; 肉食动物吃动物(包括杂食动物)
(defmethod eat((predator carnivore) (victim animal))
(format t “Carnivore –> Animal”))

;;; 捕食的植物号昆虫
(defmethod eat((predator predacious-plant) (victim insect))
(format t “Predacious Plant –> Insect”))

;;; 猪笼草吃昆虫
(defmethod eat((predator nepenthes) (victim insect))
(format t “Nepenthes –> Insect”))

;;; 草食植物吃磨菇(包括杂食动物)
(defmethod eat((predator herbivores) (victim microbe))
(format t “Herbivores –> Mushroom (Microbe)”))

;;; 微生物分解死去的动物
(defmethod eat((predator microbe) (victim animal))
(format t “Deposing dead animal (Microbe –> Animal)”))

;;; 微生物分解死去的植物
(defmethod eat((predator microbe) (victim plant))
(format t “Deposing dead plant (Microbe –> Plant)”))

下面是对eat函数进行各种测试的结果:

LISP> (eat (make-instance ‘organism) (make-instance ‘organism))
Organism –> Organism: Don’t known how to eat…

LISP> (eat (make-instance ‘herbivores) (make-instance ‘organism))
Organism –> Organism: Don’t known how to eat…

LISP> (eat (make-instance ‘ape) (make-instance ‘organism))
Organism –> Organism: Don’t known how to eat…

LISP> (eat (make-instance ‘ape) (make-instance ‘animal))
Carnivore –> Animal

LISP> (eat (make-instance ‘herbivores) (make-instance ‘plant))
Herbivores –> Plant

LISP> (eat (make-instance ‘ape) (make-instance ‘plant))
Herbivores –> Plant

LISP> (eat (make-instance ‘ape) (make-instance ‘ape))
Carnivore –> Animal

LISP> (eat (make-instance ‘insect) (make-instance ‘animal))
Organism –> Organism: Don’t known how to eat…

LISP> (eat (make-instance ‘insect) (make-instance ‘ape))
Organism –> Organism: Don’t known how to eat…

LISP> (eat (make-instance ‘insect) (make-instance ‘nepenthes))
Most of the insects eat plant…

LISP> (eat (make-instance ‘nepenthes) (make-instance ‘animal))
Organism –> Organism: Don’t known how to eat…

LISP> (eat (make-instance ‘nepenthes) (make-instance ‘insect))
Nepenthes –> Insect

LISP> _

在C++中实现Multimethod

在C++中,因为只有重载,而没有multimethod,为了达到同样的效果,你需要这样写:
void Eat(Organism predator, Organism victim)
{
if ( predator
is_a Herbivores )
{
if ( victim is_a Plant )
{

}
else if ( victim
is_a Microbe && victim.IsMushroomLike() )
{

}
}
else if ( predator
is_a … )

}
注:上面的is_a是一个伪操作符,用来判断前者的实际类型可不可以是后者(请联想instanceof)。

这 种方法称为BF法,特点是“霸王硬上弓”,一切全靠if-else和cast。维护这样的一个大型if-else嵌套结构可不是一件容易的事情。当然,如 果使用Visitor模式,肯定是可以解决这样的问题。但那样会导致问题的表达绕了很大的圈子。不直接表达就导致难于理解,而且书写起来很麻烦,扩展/修 改起来也很费力。最致命的一点,它只能处理double-dispatch问题,也就是只有两个继承体系的交互问题,对于三个或三个以上的,就无能为力 了。Multimethod则可以处理多个参数,也就是多个继承体系之间的交互问题,在参数列表中添加一个参数就可以了。

(还有一位被肠子捆住脖子的邪神LOKI,它有一种用模板来实现dispatch的方法,因为很复杂,也不太好用,这里就不再详述了,感兴趣的可以在google猛搜LOKI和独眼龙奥丁的故事)

参考资料

1、我的大脑
2、大搞学术造假的所有的院士、教授、博士、硕士们写的所有论文(这样子能帮他们增加引用不?听说帮人引用论文还能挣钱呢!)

注释

[1]关于Concept
Concept在人类的思维和语言交流之中的重要性无论如何夸大也不过份。如果没有了Concept,我们无法学习,无法描述、无法理解、无法记忆、无法祈使……
在这里我不打算完整说理,举几个例子
例1:请在不使用Concept的情况下,向别人讲解圆面积公式(提示:圆、面积、半径、PI等等都是Concept)
例2:请在不使用Concept的情况下,命令你的小孩放下手中的玩具,坐到这边的椅子上来(玩具,椅子等都是Concept)
例3:请在不使用Concept的情况下,向你的朋友描述你刚刚是如何从家中来到聚会地点的(汽车、路、交通、堵车、步行等等都是Concept)

Tagged with: , ,

如何识别注定要失败的startup

Posted in Uncategorized by Kenny Yuan on 2009/05/07

众所周知,Paul Graham只在以下四种情况下鼓励别人创业:1、经济繁荣期;2、经济衰退期;3、经济危机期;4、经济复苏期

有人创业也就会有人加入到新创立的公司中。从这些雇员的角度来看,随时识别出注定失败的迹象并及早撤离,就是一门必要的生存手段了。

那么,如何识别注定要失败的startup呢?这个话题其实挺大的,而且也是仁者见仁、智者见智。我只说几种典型情况吧。这些都是从我亲身经历的一家startup的诸多问题中选出来的。

1、画一个批萨,不要烧饼:用“最好”作为籍口来拒绝“更好”(不愿意改进,籍口是改进后也达不到最优)
2、金质胶片:在末落的产业中制造高端产品。
3、下一个山头上的草更绿:每次在一个研发方向上失败后,都认为是细分市场不足以养活一个企业,并人为设定另一个细分市场作为下一个研发方向的目标。
4、没有短痛何来长痛:用强制(或半强制)加班来应付无法完成的研发任务,认为招人的成本太大,短期内也无法产出。
5、木鱼和佛经:用同一套metaphysics的言论来拒绝员工的合理化建议。
6、花喜鹊:领导亲历亲为所有的事情。时间都用在指指点点、跑来跑去上面。同时还抱怨自己时间不够用,抱怨任何雇员都没有很好地完成本职工作。
7、肱二头肌:小股东没有话语权。
8、孤注一掷的赌徒:长远计划非常宏大,但缺乏实现步骤。赌博心理严重,要么全赢,要么全输。
9、天知地知你知我知:用企业生存为理由去说服员工,共同欺诈顾客。
10、少吃草的驴子:在招聘时,低薪不是最重要的条件,但却是决定性的条件。
11、徐郎半老:创业人为技术出身,年纪在50岁以上,过去曾有过技术上或商业上的辉煌,但是整个人的思想却落后于时代太久了。

建议的延伸阅读:Anti Pattern(http://zh.wikipedia.org/wiki/%E5%8F%8D%E9%9D%A2%E6%A8%A1%E5%BC%8F)

Tagged with: ,