为什么很多人理解不了 Max Howell 通不过白板编程面试

2015-06-15 10:04:20 +08:00
 mvj3

作为有类似遭遇的应聘者,我是非常支持 Max Howell 的。

为什么很多人理解不了 Max Howell 通不过白板编程面试

这个事件在国内也引起了很大讨论,各大社区都有,以下主要总结 如何看待 Max Howell 被 Google 拒绝? 里的讨论,态度主要分为以下两派:
1. 大多是惊讶或者调侃大牛居然连这个算法里的基础题也做不出来,有讨论具体算法细节的,也有发明段子讽刺连面试官给你放水都还帮不了你。
2. 小部分人怀疑白板面试是否真的能很好的筛选人才,毕竟没有多少项目经验的做过算法功课的应届生也可以做到通过这种白板算法面试。

很不幸的是,我并没有看到更多有分量的开源软件牛人(像 Max Howell 作出 Homebrew 这样巨作的,上了 Github 排行榜的)出来支持 Max Howell 的观点。这也说明了程序员的地位相对企业的地位而言是远不如的,更多聪明的人是选择了沉默,我也听说过为什么优秀开发者进入Google后就不参与开源了

我想这个是有本质原因的,那就是开发复杂且优雅的软件系统 和 在脑子里背下各种计算机科学的算法 是矛盾的。这不是说复杂且优雅的软件系统不需要算法,而是算法已经被工程化地内含在项目里了,项目和代码都是真实世界里存活的东西,而纯粹算法代码只是存活于阉割版(用的字符最精简)证明理论里的,只是被人们用于口头交流功而抽象出的逻辑沟通系统,但却不是用于真实业务代码的理解和修改之用的。不得不承认的是,真正能做出复杂且优雅的软件系统的人比例很少,我指的是写出真正流行的框架和语言的那些人,没错,就是你平时用的那些,所以别太指望有很多人真的能理解 Max Howell 为什么就通不过白板编程面试呢。举个有意思的例子,你会发现有名的开源软件写的代码,和通常算法里写的代码完全是两种路子,虽然从狭义的算法原理上看干的是同一件事,但是前者是富有层次的工程结构,而后者则强调把全部细节都放在脑子里直观地去一次性理解,所以这也是很多初中级程序员啃不下开源代码的原因。

在面试这种紧张(其他的还有考试和演出等)情况下 ,其考察的是肌肉记忆能力,而不是分析和创造能力。即使在面试中能写出完全正确的算法逻辑代码,那么对该人的程序创作能力也是有损伤的,所以白板编程面试是考察候选人能力的下策。

上策是考察过去项目经历,从而推断出其经验和学习能力;其次才是是通过一些实际或虚拟问题去考察。这两者都应该建立在双向沟通反馈上,才算的上是合格的面试。

最后补充一条信息,一年多前我也发过 “ 面试时候如何用 Ruby 写一个最短二分查找代码 ” 文章来描述去某公司面试时的在算法流程上的失败。当时我是写 Ruby 的,面试官是写 Python 的。摘录文章最后一小段,“最后面试官总结我应用或Rails方面的经验比较多,但是算法不太行,他准备和CEO再讨论看看。于是我咨询他公司技术部有哪些算法工作,他提了一个数据排重,然后我说如果准确度不太要求的话,可以用bloomfilter进行过滤。但是他坚持这一轮面试结束了,于是只能出门和他道别了。”有讽刺意味的是,在我加入新公司半年里就用 Python (进入公司当天我就现学 Python 干了一个统计的活了)实现了 百万题库排重算法(相同题目因为各种原因导致进入题库后有微小差异)的开源框架,源码地址在 https://github.com/mvj3/detdup ,演讲稿在 https://speakerdeck.com/mvj3/detdup 。多说几句,目前我的工作内容大部分都是如何在 Hadoop 上实现离线业务模型,并自己在写了一个不错的 DAG 任务管理框架,地址在 https://github.com/17zuoye/luiti 。对 Hadoop 的上手时间基本等同于熟悉一个 Python 类库的时间,再继续上手 Spark, Storm 也是不在话下,而这恰恰是有些看上去熟悉算法的人不容易做到的。

关于 Invert Binary Tree 的技术讨论

首先是 [Invert Binary Tree] 的递归版本,虽然我之前从来没有做过这个程序,不过在 LintCode 上读了需求后,用 Python 第一次写就通过了,http://www.lintcode.com/submission/630731/ ,但是脑子里还是慢慢反应一下,甚至还不太自信。

其次是 [Invert Binary Tree] 的非递归版本,即是用栈实现的,我没想出来,主要想法是因为每个节点最多有两个子节点,是不是用数组来模拟索引啥的。因为不是实际想做的,所以和平时工作学习一样,直接搜索了,是用 Stack 实现,其他逻辑大概稍微复杂了一点。总之,凭我的经验,是需要一些时间来突然顿悟这个算法是怎么回事的,但是我知道过段时间我肯定忘了,所以就不想了。

最后是关于 Google 面试官考察的到底是不是 Invert Binary Tree,我看到 Max Howell 给某人的回复是 "min-max the tree, ascending to descending",地址在 https://twitter.com/mxcl/status/608891015945170944 。这样听上去好像是要做个上下翻转,而不是左右翻转,这可能是 Max Howell 没说清问题,也可能就是本身问题描述就不是一下子能说清的,那么请考虑在如何实现时就需要去理顺更多细节,可想而知会更容易出错了。

傲娇的人只是对事情更认真而已

有些人说 Max Howell 是大牛,被考察一个简单的 Invert Binary Tree 太不上档次了。

对于这个问题,我首先强调的是,优秀是一种习惯,同样一个人的代码风格往往是一致的,更深一点理解,TA 解决问题的范式都已经形成了自己的一套经验体系。所以大牛去写一个算法,通常是工作中的,那么必然是考虑到各种和其他模块如何整合,API 是如何的形式,而自身的代码组织逻辑又是如何自恰。举个象棋或围棋的例子,虽然规则大家都懂(这个类比到算法的步骤),但是大师看布局的角度和一般人的是完全不同的(这个类比到有人觉得 Homebrew 没啥复杂技术,可是却不是一般人能做出来的),而且也不太好解释(要知道机器至今在围棋成绩上仍远远落后于人类水平)。所以突然把大牛放到一个陌生的工作模式里,即是换成教科书的算法步骤思维,并且是面试的场景,而且是短时间,做不出来是很正常很可以理解的事情。要知道真正一个算法整合到实际项目里是得花很长时间的,里面涉及到不断的反复构思和调整。

有些人说 Max Howell 傲娇了,但他其实已经做了算法面试准备了(A good thing that came out of it is, I did prepare, and actually I found a bunch of common algorithm problems a lot of fun. Will continue. https://twitter.com/mxcl/status/608754975364276224 )。而且大家得注意一个事实,工作多年的人去复习算法和训练专门用于测试的思维方式是有时间成本的,相对来说应届生的时间成本就少多了,专门做算法的人在此不谈(TA们的软件工程水平相比而言也是低一点的)。算法确实很有意思,这两年我也在提高,它仅仅是解决问题的关键之一,而且大部分场景下,刻意的教科书算法对于工程来说都不是关键的。

说一下我个人对编码面试的观点,我热于在实际工作中接受任何相关项目的挑战,但是我不想做无用的压抑的耍猴游戏(可能也和我不喜欢玩电子游戏有关)。

面试时气场不合是个关键问题

很多时候人都是相当情绪驱动的,我人生经验里有一个让我震惊的观点是,有些我认为是应该非常理性的人居然也很坦率地承认自己对这个问题就是感性的。Max Howell 的气质是偏艺术家型的(这里有他在 Github 的演讲视频 https://www.youtube.com/results?search_query=homebrew+max+howell ),我猜测 Google 的那个面试官是偏死板的工程型的。所以如果和面试官气场不和,我理解是主要是TA感受不到你在TA未来的控制(中性词)范围内。

我一直有个疑问,对于一些很有想法的人(理解 Max Howell 的个性看他个人主页的色彩设计就知道了 http://mxcl.github.io/ ),面试官在看到简历时,为何不直接拒绝呢。还有另一个可能性是,一个面试官去面试可能是别人要求去的。如果让对方来了,为何大家不和和气气好好沟通呢,面对一个可能是未来的同事,非得拿那些刁钻的题目来为难对方呢(举个人人努力一下都会做的算术题,比如 7*19 比 200*300 难多了,虽然同是两个数,而且前者数还小),工作应该是快乐和有激情的啊(有些人其实是在组织里很压抑的)。

举一个我认为相当侮辱人的程序员段子,美女来面试,连不会写 HelloWorld 都能过(所谓程序员鼓励师),长相不好看的来面试,一定得拿红黑树来压制。虽然大家都当是个笑话,可是也折射出一些潜在的不良价值观。我理解 culture fit 是个一直都存在的常见问题,但是我还是觉得工作能力优先,这样才不会劣币驱良币。有一个调查是全球盲人比例约为千分之五,而城市道路上基本都人性化的加上了盲道,从道德上来说,应该谴责强势雇主在性格上歧视应聘者,另外我也相信大多数人还是知趣的吧 :)

其他一些关于面试的问题

我看到一篇看上去很好的文章 http://www.gayle.com/blog/2015/6/10/developer-interviews-are-broken-and-you-cant-fix-it ,主旨是讲面试流程无论如何改进,都是有缺陷的。我的想法是,这真的是典型的逻辑式废话,从各种利益权衡来证明现有的制度是合理的和没办法的,同时为了公平性而不能更改任何细节。

我唯一的想法是,很直观的,一个大牛活生生的站在你的面前和你善意地沟通,同样希望得到一些相互的尊重,而面试官却非得照着一个脆弱的流程,去让自己和大牛都去适应这个规范(虽然实际工作时大家都是很灵活的),让旁观者也是干着急啊。

有必要我可以逐条反驳文章里我认为不妥的意见。

P.S. 我的知乎回答在这里 http://www.zhihu.com/question/31202353/answer/51304584

16199 次点击
所在节点    程序员
48 条回复
RyNex
2015-06-15 10:59:57 +08:00
赞同楼主的观点
mthli
2015-06-15 11:18:43 +08:00
写得挺好的,我也有类似的经历。赞一个~
sanddudu
2015-06-15 11:24:21 +08:00
我目前看到过的最客观的分析
msg7086
2015-06-15 12:38:35 +08:00
考算法更多的是看的分析问题的能力而不是背题。
分析问题的能力,不管在算法题也好在实际开发过程中也好,都是有用的。
原题应该是BST反序,所以就是让你通过BST反序来推导出实际要做的题目(左右对调二叉树)。
至于编码的过程其实真的不值一提了,远远比你之前Ruby二分搜要简单。
在5分钟内写完递归版本并不过分吧

def invert_tree(root)
》 return nil if root.nil?
》 root.left, root.right = invert_tree(root.right), invert_tree(root.left)
》 return root
end

我问你,如果你面人,这3行代码都写不出的人,你给过吗?
msg7086
2015-06-15 12:46:01 +08:00
做开发的,大致有这么两类人。一类主要是应用方面的,把别人的轮子组件拿来,加上逻辑然后做成软件。网站开发、常见的应用软件开发,都属于这种。还有一类主要是做轮子的,比如写gem写lib的等等。
对于前者来说,算法并不是那么的重要,更多的是软件的设计,界面的设计,功能的完善。
而对于后者来说,算法就直接导致了下游开发者的重大变化。(比如隔壁帖子里一个gem在实装了链表功能以后瞬间牛逼起来就是很好的例子)算法不好当然也可以做基础开发,但是算法好可以让你锦上添花。

另外,谷歌是个很神奇的地方,大家都挤破脑袋想进去,那么谷歌势必要提高招人的门槛,否则门槛太低了这就没法过日子了。FLAG系的几家公司基本都这样。
imn1
2015-06-15 12:46:55 +08:00
我以前面试别人也会考白板编程(其实我也搞不清算不算,以前“没”这个名称)
但为了避免应聘者紧张不能体现其真实能力,会事前告知这个白板编程的几个标准:
1.不考核程序运行结果的正确性
2.也不考核其中的语法正确性
3.这个程序不会上机测试
就是写错,运行不了也没关系,考的不是结果

考的是应聘者用了什么结构和思路(要他说明,这个录音,比看他的程序更重要)
这个某种程度和以前一篇文章的科考类似

然后是项目经验,我一般直接对应聘者说希望对方诚实回答
告诉对方面试不是最重要的,试用期才是最重要的,面试只不过是因为公司没办法对所有应聘者都给予试用期,而定的筛选步骤。面试时是高材生,试用时是文盲炒鱿鱼,只会弄得大家都不愉快,也浪费应聘者寻找合适工作的时间

不过这些都是很久以前的事了,不知道现在的社会是怎样
adjusted
2015-06-15 13:07:10 +08:00
我不明白他是怎么知道自己就是因为这一个问题被拒的。
aphantee
2015-06-15 13:13:46 +08:00
@msg7086 正是这个道理,这个问题用递归瞬间解决,基本不用动脑子。所以我也认同‘放水’说,估计当时面试人员也惊了。
yangff
2015-06-15 13:19:15 +08:00
简单的说,这道题,你向一个高中生说明一下什么叫做树,他都能给你口述出该怎么做,这种题做不出来,不是能力和刷题的问题是智商问题,所以才让人感到奇怪。
yangff
2015-06-15 13:27:50 +08:00
btw, 我觉得至少给出这样的非递归实现才是比较好的, 栈模拟或者队列模拟还是反映了思维不灵活.
当然我认为这样的代码不容易在面试的时候写出来, 但是传统的实现不应该有问题.
https://gist.github.com/Yangff/d8040a4f27416d74b0f4
GtDzx
2015-06-15 13:32:59 +08:00
不明白为什么很多人把算法能力和系统能力对立起来,甚至还有像LZ这样认为的:"那就是开发复杂且优雅的软件系统 和 在脑子里背下各种计算机科学的算法 是矛盾的。"
mvj3
2015-06-15 13:37:52 +08:00
@msg7086 你可以仔细看下 "关于 Invert Binary Tree 的技术讨论" 部分,实际面试时应该会让写个非递归版本的,否则面试官也不会去出这个题目。

另外我是你提到的两种开发皆修的,具体可以看我的个人资料(我的博客和 Github),。但是我的算法能力和有些思维非常“严谨”的人还是比不上的,比如讨论问题时,我可能跟不上,虽然我对算法复杂度判断能力还是挺好的。

@imn1 我非常赞同你的方法,我也是这样的,我想大部分懂情理的人也是这样的,可是现实是有很多误会或认知上的不一致。
50480350
2015-06-15 15:01:17 +08:00
看完LZ的回答发现知乎已经恢复了。。。
caiych
2015-06-15 15:08:06 +08:00
@mvj3 在Max Howell给出"min-max the tree, ascending to descending"之前还有疑问是左右翻还是上下翻,给出了之后应该说大家才确定了左右翻,就是这样的一个简单题(递归版本)。

这样的简单题实际都不算是『题』了,跟见面的自我介绍和聊聊简历是一样的;之后可能的第二问改成非递归版本才是『题』。不过看起来Max递归版本就没写出来…(他没提)

其实面试官应该在卡住太久就给出提示或者换题了的(应该有,不知道是没换还是他没提)。
不过我有限的经历里听说过warm up做满了一个小时没时间做题了的,确实是没听说过warm up卡太久把题换了的。
Heartwork
2015-06-15 15:13:20 +08:00
这TM有什么好讨论的,你们上过数据结构课程么?

你们听说过树的遍历么?
这个题目就是在树的遍历的基础上加了一点点难度而已。为了放水放的不那么明显,面试官也是操碎了心啊。

还有用栈的那个,那是树的非递归版本的算法,作为当时的一般课堂作业,同时附赠一个树的层次遍历,用队列的。

好好翻翻自己扔掉的数据结构教材,你就知道这个题目仅仅是毕业生水平。
binux
2015-06-15 15:23:06 +08:00
1、Github 排行榜只能说明流行程度
2、Homebrew 并不是复杂的软件系统
3、如果你连用栈模拟递归都需要「顿悟」的话,说明你根本不理解递归,甚至不理解函数是怎么执行的。
LuoboTixS
2015-06-15 15:29:51 +08:00
@Heartwork 是,就是一个本可以水过拿offer的喜剧愣是飞了,同时搞得众人皆知
lonelinsky
2015-06-15 18:58:13 +08:00
@msg7086 FLAG, F = facebook, A = Apple, G = Google, 那L到底是啥? 总不能是LinkedIn吧 =。=
mvj3
2015-06-15 22:36:37 +08:00
@caiych 首先多谢你,我对这个题本身要求的需求再思考了一下。"min-max the tree, ascending to descending" 意思是这是一颗从小到大的树,然后去升序到降序。我的疑问是 min-max 和 ascending to descending 不是有点同一个意思吗。好吧,我对树结构的各种理论并不了解,英文水平也是半吊子,所以就不说这个了。

比起这个,我觉得还有个更有意思的问题,凭我的经验来推测(仅仅是我的推测),递归版本实现了,但是非递归(用 Stack 实现)没实现。我想说的是,我也是刚想明白整个流程,昨天看了微博上 左耳朵耗子 的 C++ 实现,只明白用了个栈来做临时存储和协调者,而节点在里面的进出并没有看懂。刚才重新看了一下,才算是真的明白了。你是不是同样会觉得有点吃惊呢?!

我也说一下自己除了上面提到的 detdup 之外的完全由我自己独立设计和实现的算法相关的开源作品:
1. https://github.com/mvj3/fill_broken_words 填破碎单词,Python实现。
2. https://github.com/mvj3/phrase_recognizer 英文短语识别,Python实现。
3. https://github.com/mvj3/region_unit_recognizer 识别 带有省市区等地址的 企事业单位,Python 实现。
4. https://github.com/mvj3/hangman 猜单词游戏,Ruby实现,两年前的一个做了两天的面试题。

这四个都是非常有意思的问题,就我实际做的经验(很完整的工程实现)来看,1 和 2 较难,大概是两周,3 和 4 相对容易,应该都是两天。前面三个都是由树结构实现的,最后一个是字典实现的(其实有人做了更好的基于树实现的)。

所以这里有个有意思的问题就是,我不能马上看懂二叉树的栈实现,但是我却可以做出以上,不证明了白板面试是非常随机和带偏见的一个测试吗。


@Heartwork 最好是你可以提供一个基于某算法实现的开源项目(这样大家都可以看到,否则纯算法显示不出工程能力),并具有良好的工程结构,不然懂算法有什么用呢,你说是吧。


@binux 1、Github 排行榜只能说明流行程度 --- 不过上了一定代码量,并很高 star + fork 的,那基本都是货真价实的了。或者你可以举个反例看看?
2、Homebrew 并不是复杂的软件系统 --- 复杂不复杂都是相对的。不过我愿意承认我大概翻过 Homebrew 代码(当然没有像实际工作项目那么认真,花了很多时间),除了 Formula 的格式,其他基本都没懂。

3、如果你连用栈模拟递归都需要「顿悟」的话,说明你根本不理解递归,甚至不理解函数是怎么执行的。

可能是我文章里说的不够清楚吧,我说的"顿悟"指的是 while 里面的部分,不过这个回复的上面已经解释了我理解这个算法的整个事实过程了。理解用到 stack 确实是即可的,因为我平时工作就经常用到这个技巧。但是 while 里面的逻辑整个放到脑子里,在我的思维里真的是顿悟的,我想这个是人们通常的被别人指点一下的那种体验。当然很可能是我的思维比你的慢,即是 CPU 慢点;当然你也听说过人类最近用成千上万台机器才勉强识别出了猫,所以CPU再快也没用。

我不清楚你对递归和函数的了解水平,但是我还是愿意给出我的理解,虽然可能有错。

什么是递归呢,在我看来,它是人脑思维的一种,和时间空间等一样神秘。人类发现了数据的递归模式(我递归论证了)模式,利用一些可用于稳定执行逻辑的物质(比如CPU器件)来做操作。

函数如何执行呢,摘录一段我在 [The Design Draft Of Human Programming Language]( http://human-lang.org/ ) 的思考笔记 "计算机在本质上就是 过程 + 数据,用 CPU (central processing unit) 来操作 IO (input output) , 也即是用布尔逻辑操作二进制数据。布尔逻辑包括基本的 与,或,非,再进而构建出 与非,或飞,异或 等复合操作(建议通过看 Wikipedia 上的 逻辑函数表示法 得知其相互关系),再进而构建出 集合交集,访问数组某索引的值,等常用数据结构的操作,最后进而构 建出万能的 函数 操作。数据在计算机体系里都表示为离散的 0 1 数字串,进而构建出 Boolean, Integer,Float,Char 等基本数据结构,再进而构建出 String,List,Set,Dict 等高级数据结构,最 后构建出类继承系统,JSON,XML 等复杂业务数据结构。",不知道这样可否让你明白我的理解。
Heartwork
2015-06-15 22:55:08 +08:00
@mvj3 首先,如果你不是CS专业毕业的学生,那我可以说你很了不起。

但是如果你是,那你回去看看自己的数据结构教材还在不在,如果在的话,看一下关于二叉树的重点部分和习题部分。

二叉树的非递归形式是使用手工的堆栈模拟递归过程调用中的栈帧,以目前这么发达的网络,你应该能找到递归调用过程&非递归调用过程的单步flash。

以数据结构课程在CS的重要程度,以及树的几种遍历在树算法的入门程度,只要你上过这门课,这个肯定会有印象。

印象里树节点的删除算法以及后面的二叉平衡树的rotation可能稍微复杂点。

我回答问题是就事论事,你贴上去四个project也改变不了我的结论。

BTW,我最近看了<write your own lisp>之后倒是写了一个lisp的解释器,有兴趣不妨看看:
https://github.com/Heartw0rk/lisp3

这是一个专为移动设备优化的页面(即为了让你能够在 Google 搜索结果里秒开这个页面),如果你希望参与 V2EX 社区的讨论,你可以继续到 V2EX 上打开本讨论主题的完整版本。

https://www.v2ex.com/t/198594

V2EX 是创意工作者们的社区,是一个分享自己正在做的有趣事物、交流想法,可以遇见新朋友甚至新机会的地方。

V2EX is a community of developers, designers and creative people.

© 2021 V2EX