算法题不是检验程序员的唯一标准

这段时间很多公司的面试都在拉开帷幕。前几天我的一位好朋友应聘一家被誉为 “工程师天堂” 的互联网公司,第一轮面试就悲剧了。原因可能是在一道关于树的算法题上耗了一个小时。我觉得凭一道算法题断定一个人的能力过于僵化了。当然,这样的公司可能应聘的人太多,没时间仔细考察每个应聘者。

IMG_20130928_201318IMG_20130928_201318

今晚同学带我去清华玩,顺路逛了清华科技园,也算我进过这家令无数程序猿向往的公司大门了。回来的路上,我就在思考纯算法题面试可能的弊端。在 coolshell 上看到了跟我观点类似的文章《为什么我反对纯算法面试题》《我是怎么招聘程序员的》《再谈“我是怎么招聘程序员的”》,于是鼓足勇气把自己 simple & naive 的观点拿出来与诸君分享。欢迎各种形式的拍砖。

算法题实用吗?

算法固然重要,一个不懂算法的程序员很难写出高效的程序。不过,依我浅见,算法题里的算法跟学术研究和工程实践里用的算法是有很大区别的。算法题就像是 “脑筋急转弯” 式的小学奥数题,做过相应题型就知道解题套路,没做过的就很难在短时间内想到点子上。但真正的数学专业知识却离小学奥数题很远。

举个例子,计算机竞赛中的最大流问题难倒过不少人,这个问题看起来也是来源于实际。但试问有多少研究方向用到了最大流?哪种产品里用到了最大流算法?也许只有某些细分领域能用到,此时朴素的最大流算法已经不够用了,还是得查文献。

Coolshell 上有个更合适的例子,搬运过来。“找出数组中第2大的数”,这个问题从纯算法的角度显然有 O(n) 的解,但一个经验丰富的工程师更可能把数组排个序再取第2个。因为在实际产品中,很少有只需要找一次的情况,与其每次查找都 O(n),还不如 O(nlogn) 排好序,插入删除元素时 O(logn) 维护有序数据结构,查找第2大的数就是 O(1) 的了。这种数据结构还不用自己编,用库就行了。更重要的是,以后的需求可能不是找第2大而是第4大了,这个 “找第2大的数” 的代码能重用吗?

这个例子想说明的是,工程中很少有人会用 fancy 但很复杂的算法,一般都是 simple and just works。如果一家公司的员工都在产品里绞尽脑汁地设计 “渐进复杂度低” 的算法,工程师们也许觉得很有挑战,每天都很充实,但代码是不是可维护就不一定了,保不准某天还会爆出一个致命 bug。

解不出算法题就不是好程序员吗?

当然,算法题不会没用,它就像小学奥数题一样,有 “思维体操” 的作用。从统计意义上看,学过奥数和没学过奥数的相比,练过 OI/ACM 的和没练过的相比,在思考和解决问题方面的确有不少差别。也就是说,能快速而准确地解出算法题的一定是好程序员。不过,“思维体操” 不只一种,练哪种操都能强健身体,我们不能武断地认为没练过 OI/ACM 的就不是好程序员。事实上,大多数在计算机领域作出重要贡献的年轻工程师在他们上学的年代已经有 OI/ACM,但没有参加。

大学计算机专业远不只是学习算法,还有很多重要的内容,如计算机体系结构、操作系统、编译原理、计算机网络。一个不懂最大流算法的人完全可以玩转程序语言设计,一个不会写编译器的人完全可以玩转各大 OJ(Online Judge)。更有一些 EE 和数学系的本科同学,他们根本不懂什么链表、树,但在计算机领域的顶级会议上已经发表了 paper。不懂数据结构的人怎么组织和处理这么多数据?

原因在于,现在的编程语言越来越高级,库越来越丰富,计算资源越来越廉价。他们很可能不知道 Python 的字典用的是 O(1) 的 hash 表,很可能不知道 C++ STL 里的 sort 是 O(nlogn),很可能不知道 SQL 查询是如何不全表扫描就得到结果了,很可能不知道 Hadoop 的调用栈打印出来有多少张纸……但他们站在前人的肩膀上,哪怕浪费一些计算资源,一样能做出牛逼闪闪的成果。

Here is the point. 每个人都有擅长的技术领域。好的面试,应该充分展现每个人擅长的技术领域,而不是用 “算法题” 这一把尺子去衡量所有人。

又一种应试教育

拿纯算法题来考察应届毕业生,还有一大弊端:大学计算机教育有变成应试教育的危险。所有立志做程序猿的同学都去练这些题,谁练得多、练得好,谁就拿到 offer。诚如是,大部分专业课不重要了,软件项目实践不重要了,学习沟通能力也不重要了。

面试官?考官?

之所以算法题如此盛行,除了评判简单、“识别率” 高以外,还跟面试官的 “考官” 心态有关。两年前我在面试少年班学院学生会技术部的时候,就抱着 “考官” 这种居高临下的心态。表面上我是想招到技术基础好、“来了就能干活” 的人,但内心里事实上有一个声音:瞧你们这些新生,XXX 都没听说过!因此,当年我出的 “面试题” 现在看来是很荒唐的:(下图截取部分题目)

jishubu-0jishubu-0
jishubu-1jishubu-1

面试结束后我 “很满意”,其实是满足了小小的自负心理。我忘记了来面试的都是大一新生,人家又不是来应聘工作,怎么可能恰好有跟我一样的能力?况且这些所谓的 “能力” 大部分还是大一这一年在技术部折腾出来的,不过差一年经验,有什么了不起的?

后来,我跟技术部的同学们聊天,渐渐意识到每个人都有独特的经历和经验,他们中学期间做的很多东西都是令我非常惊奇的。面试的时候他们怎么没说出来呢?应该是我弄的这篇 “题” 吓到了他们,感觉 “一入少院深似海,从此中二是路人”,从而没敢说出自己独特的经历和经验。

算法题很适合面试官摆 “考官” 的谱:答案我早就知道,等着你犯错,我看笑话。面完几个人下来,面试官会感觉自己比那些来面试的人聪明多了。因此,要避免纯算法题成为唯一标准,首先要放下 “考官” 的架子。面试的目的是找到未来的合作者,而不是验证自己智力上的优越性

让面试者主导面试过程

Joel 在著名著作 Smart and Get Things Done 中说,面试是否通过应该取决于自己是否愿意与眼前的这个人共事。即使一个人技术再强,如果不愿意与眼前的这个人共事,则不应该给他通过,而不是想 “给他过吧,也许其他组会喜欢这个人”。

基于此,我设想的一种方案是,让面试者主导面试过程。面试者把这一个小时想象成是说服一个客户的时间,任务就是说服面试官与他/她共事。如果算法能力强,可以选择答算法题;如果项目经验丰富,可以交流做过的项目、写几行相关代码;如果学术造诣深,可以交流做过的研究课题。不管什么形式,反正要在一个小时内向面试官证明自己的能力。面试官当然也不是旁观表演,如果面试者不能充分证明他/她的编程能力,可以要求他/她写几行代码,问题可以与面试者感兴趣的技术领域相关。这与一上来就给一道随机抽取的 “算法题” 是完全不同的。

之所以提出这么大胆的 “改革方案”,是因为我经历过这种类型的面试:(详情参见我的《MSRA 面试总结》)

  1. 面试开始是自我介绍,面试官在翻阅我的简历。
  2. 因为我在简历里写了一些比较好玩的项目,面试官就问了很多技术细节,我也有机会把这些项目的开发经历、技术挑战和心得体会讲出来。
  3. 面试官问我哪门专业课学得最好。这里就是把选择给面试者,让面试者展示自己最优秀的一面。
  4. 因为当时答的是操作系统,面试官让我讲解内存管理机制。当时我说,Windows 上的我不懂,面试官就说讲 Linux 上的,讲你懂的。如果面试官采用 “算法题” 的模式,要我讲解 Windows 上的内存管理机制,后果可想而知。
  5. 为了验证我的编码能力,让我写了个与操作系统和体系结构有关的小程序。这如果算是个 “算法题” 的话,比 “合并两个有序序列,找出第K大的数” 之类的题更有针对性。
  6. 面试官介绍他的研究方向,并问我对读博士的看法。这可以认为是个 “双向面试” 的过程,也让面试者感到足够的尊重。
    一轮面试下来,不仅感觉自己的能力得到了充分的发挥,还感觉自己受到了尊重。优秀的公司和优秀的工程师是双向选择的,冷冰冰的算法题会让面试者感觉这家公司也是冷冰冰的。

一个都不能少

尽管很理想化,我希望一家优秀的公司不应该错过任何一个前来应聘的优秀工程师。

我们看到,算法题中的高级算法大多不能直接应用到研究或产品中,解不出算法题的也未必不是好程序员。现在的编程语言越来越高级,库越来越丰富,计算资源越来越廉价,工程师未必对性能问题锱铢必较,而是投身自己感兴趣的细分领域。

因此,希望不要把算法题作为检验程序员的唯一标准,尝试把面试过程更多交给面试者,引导他/她充分展示个性与风采。换一个心情,也许我们能领略到别样的风景。