点击上方“CVer”,选择加"星标"或“置顶”
作者:胡津铭
全文接近4万字,可以根据目录各取所需。
面试心得2019年春招和秋招,我在中国进行了多场面试,其目的是找一个暑期实习职位和找秋招的正式工作。这是我的个人心得总结。
在进行了比较与思考后,我最终选择的公司是Optiver,职位是Low-LatencySystemDeveloper,工作地点在上海。文末也会阐述选择的理由。
另一方面,我身边确实有一些这样的同学:他们相当有实力,但是却因为种种原因无法在面试中展现出自己的全部实力。事实上,不同的企业有不同的面试文化,比如Google的面试官希望面试者能成为一个他愿意一起工作的同事,字节跳动的面试官也许希望面试者是一个数学、算法、coding、工程都不错的全面人才,这样的人才更可能成为一个“能解决问题的人”。但是,作为面试候选人,我们其实没必要去针对各家公司的文化对症下药:应对面试应当有一些共通的要点。在我看来,面试最关键的一点在于面试者要意识到这不仅是一场测试,更是一次需要充满着沟通与交流的谈话,让面试官认为他/她愿意成为你的同事,希望每一位面试者都能尽早明白这个道理。
编程部分心得在面试过程中,面试官常常会给出几道算法问题,需要面试者提供思路或写下代码。在大多数公司的面试中,这一部分的表现都非常重要,而对一些外企来说,这部分的表现是具有决定性的(甚至是唯一重要的表现)。对于这部分的准备,首推LeetCode等网站,这里不再赘述。再提几句话,对于一些重视算法问题的公司如Google,hulu,airbnb,微软,头条等,不要抱着可能撞到原题的心态去准备,很难撞到原题的,对于这些公司,你需要做的就是反复练习提升自己的能力,而且由于题目较难,需要有较多的训练量。而另一些不是很重视这类问题的公司像阿里、腾讯什么的,则刷一些常见的题目就很可能撞到原题了,而且难度一般不大。因此,根据target公司的不同,可以有不同的准备方式。下面将列举一些其他在面试中我认为比较关键的点。
一个非常简单的例子这里先给出一个非常简单的问题,下面的关键点将结合这个问题来阐述。该问题为,计算一棵二叉树的高度。简单的实现如下:
intgetHeightOfBinaryTree(TreeNode*root){if(!root)return0;intleft_height=getHeightOfBinaryTree(root->left);intright_height=getHeightOfBinaryTree(root->right);returnmax(left_height,right_height)+1;}练习白板编程面试的编程部分往往是白板编程:面试官要么要求在一个类似于GoogleDoc的地方写代码,要么就是干脆在白纸上写代码。这种情况下coding的体验与平时使用IDE的体验是完全不同的。以GoogleDoc为例,许多人(比如我)一开始甚至很难写出能编译的代码,更别说一遍写出bug-free的代码了。同时,没了IDE,debug的难度也会大大增加。而在白纸上写代码的难度则还要更进一步。适应白板编程的方法也很简单,只需要足量的练习即可。
问清题目问清题目至关重要。如果你对面试官的编程问题理解得不清晰,那你应该立刻问一些能帮助你理解的问题。例如:数据范围是多少?这个数组的大小范围是多少?能不能给个样例?如果输入是这个,那输出应该是什么等等。在上面这个简单的问题中,可以问的一个问题是,二叉树的高度是什么(据我所知,高度的定义并非所有教材都一致)?
许多面试官在面试的时候,会故意先抛出一个模糊的问题。实际上,他们希望面试者能够经过一些询问理解问题。在这个过程中,面试者能够展现出自己对问题的分析能力以及沟通的能力。前者的重要性参见编程珠玑第一章:明确问题,战役就成功了90%。后者的重要性在于,问清题目的这个交流过程与面试者入职之后与同事讨论问题的形式非常类似。显而易见,一个能够很难沟通的面试者也很难成为一个很好沟通的同事。
与面试官确认函数签名确认了题目之后,我认为合理的做法是先和面试官确认函数签名,也即输入是什么参数,输出是什么参数等等。这一步的代价很低,而且相当重要。第一,这可以告诉面试官,你对函数签名的设计相当重视,而这一点在实际应用中很有价值。第二,这可以进一步帮你确认自己理解了题意。一个合理的函数签名可能就类似于LeetCode题目里的函数签名。上面代码中的签名就是一个比较合理的签名。
如果你认为这个问题与某个经典的问题思路一致,或者可以用到某个经典的算法,那么就直接点出来。例如计算二叉树的高度,实际上是一个后序遍历,那么可以直接点出来。
确认边界处理在开始写代码以前或者是写代码的过程中,一定要思考代码的边界条件。最典型的边界条件有:数据是否会溢出?指针是否可能为空?链表是不是可能存在环?数组的长度是不是零?输入的数据会不会完全不符合题意的要求?在示例中,边界条件就是当结点指针为空时,高度应该是0。当你察觉到边界条件存在时,就可以询问面试官处理方式,或者直接告诉面试官你认为什么样的处理方式是合理的。对边界条件的处理在开发软件时也异常重要。忽视了一个边界条件,就会对程序鲁棒性造成极大的影响,可能直接造成巨大经济损失甚至是人员伤亡。
代码中使用可读性高的变量名和函数名在写代码的时候,尽量使用可读性较高的函数名和变量名。例如,要计算二叉树的深度,函数签名可以为intgetHeightOfBinaryTree(TreeNode*root)入参就叫root(而非node)。递归时,左子树的高度的变量名可以叫left_height。诸如此类。这样操作的主要目的也是让面试官看到你良好的编码习惯。
写代码过程中不断与面试官交流实现算法的过程中,切忌闷头狂写而不与面试官交流。实际上,在写一些关键代码的时候,你完全可以告诉面试官你在实现什么功能。同样如前例计算二叉树深度,那你就可以告诉面试官,intleft_height=getHeightOfBinaryTree(root->left)是在计算左子树的高度(良好的函数名和变量名其实也让这行代码不言自明),而introot_height=max(left_height,right_height)+1则是根据左子树和右子树的高度计算当前根节点的高度。
当然了,在这个简单的示例中,交流或许显得不是那么重要,但是在一些复杂的问题中交流可能会非常重要。例如,示例的follow-up是请不用递归实现同样的功能,或者更进一步,请用常数空间实现同样的功能。在这样的问题中(代码可能长达数十行),交流就至关重要了。面试官需要和你交流来理解你的思路与状态,你同样需要交流来理清思路。这种写代码过程中的交流也是正式工作时非常重要的能力。
写完代码后主动测试在你写完代码之后,不要急着告诉面试官你已经写完了。最好先手动跑一个/数个简单的样例。注意跑这个样例的过程要让面试官可以看见并轻易地理解,这常常是需要一些练习的。例如,我在GoogleDoc上跑样例的做法是,在屏幕上写出中间变量的当前取值,然后用鼠标光标告诉面试官现在程序跑到了哪一行代码,当前各个变量的取值是多少等等。主动测试的好处有很多。第一,这告诉面试官你很重视测试,而测试在实际生产中是非常非常重要的。第二,一个简单的样例常常可以找出不少类似于typo这样的小错误。第三,如果你的样例给得不错,那你甚至能够借助这个样例找到程序中的bug并纠正它,这总是要好过面试官发现并告诉你程序中存在着bug。主动测试时,你也可以确认你的程序可以很好地处理边界数据。
计算机基础部分心得面经的使用计算机基础部分的内容包括数据结构、操作系统、编程语言、计算机网络等等。这部分的准备很大程度上是需要一些扎实的基础的,再配合一些面试公司的面经。有些同学想仅仅靠看面经就应付过去,我可以说大多数情况下是不太可能的。有经验和水平的面试官可以轻易地通过几个follow-up问题来判断出来这名候选者是不是靠面经回答出来前面的问题的。当然了,面经对于这块内容仍然是非常有价值的,但阅读面经的时候要注意,并不能仅仅看一下某道题目的答案就够了,而是要看这个题目考察的是哪一块的知识,这一块知识自己有没有遗忘的、生疏的、不扎实的,如果有的话要去做相应的准备。面经是告诉你这家公司面试的时候喜欢问哪些知识,而不是告诉你他们喜欢问哪些特定的问题,虽然有的时候有些高频问题确实可能在你的面试中出现。
说出自己的insight如果针对某个问题有自己一些独到的见解,或者是这个知识在很多教科书上可能看不到,很多同学也不一定知道,那么在回答问题的时候说出自己的这个insight,当然前提是自己的说法是有道理的。这里举一个简单的例子,比如一个面试问题是,可以用什么数据结构来实现队列。回答可以说是链表,接着可以补一句但是链表实现队列的性能不一定很好,因为链表节点的地址空间不是连续的,对cache不友好(小问题:那么如何改进这一点呢?)。这种知识其实是有一些经验的人或者基础扎实的人都知道的,不算是什么难点,但作为应届生,能直接说出这一点还是可能会让面试官觉得这个候选人基础不错。
结合自己的使用经验阐述如果在某些基础问题上自己有一些实际经验,那么可以结合自己的经验来回答,这样会让面试官觉得这个候选人不仅基础扎实、经验丰富,而且学以致用、分析问题的能力也挺强的。
这里举一个简单的例子,比如面试官问hashtable处理冲突有哪些常用的方式,各有什么优缺点。那么可以回答常用的有线性探测和拉链法两种。如果自己有相应的经验,那么就可以结合经验谈谈优缺点,例如线性探测在实际使用的时候常常需要空间开得比较大,hashtable的装载因子需要维持一个一直比较小的状态(比如25%-50%这样),否则的话性能就会很差,因为查询和插入都会频繁地进行长距离的线性探测。而拉链法对空间的利用效率就会比较高。在提供足够的空间的时候,按经验线性探测会比拉链法快很多,比如之前做了个项目,在满足空间条件的时候线性探测会快7倍左右(这是在结合经验谈),原因是线性探测比拉链法对cache更友好(这是基础知识)。
类似于这样的回答方式,可以让面试官留下一个很好的印象,认为这位候选人的整体素质也非常出色。
项目部分心得简要介绍项目背景如果面试官是很熟悉这个领域、这类项目的人,那么你可以makesomeassumptions,即不需要做多少背景介绍。否则的话,还是建议简单谈一下自己项目的背景是什么。这是因为在不同的背景下,同一种功能的实现常常会有不同的选择。这样的背景介绍能帮助面试官更好地理解这个项目,以及大概理解一些实现的选择。背景主要包括场景、问题定义、需求、自己负责的部分扮演的角色等等。
介绍项目的approach介绍完项目背景后,需要简单介绍一下自己这个项目的解决方案。解决方案主要是使用了什么技术、什么工具、怎么样的实现等等。需要注意的是,介绍解决方案的时候最好要结合场景一起说,否则会缺乏一些说服力。
这里仍然举个简单的例子。例如做深度学习的落地,深度学习框架选用的是腾讯的ncnn,那么最好说一下因为场景是嵌入式arm设备,且没有显卡,在这种场景下,ncnn做了很多指令级的优化,速度会更快。
指出项目中的困难点和解决方案针对项目中的困难点要特别认真地谈论一下,需要介绍为什么这个点是个困难点,解决方案大致是什么样的思路,为什么要这样去设计解决方案,最终达成了一个什么样的效果。如果一个候选人能展示出准确的痛点、瓶颈分析能力,并且能提出合理的解决方案的能力,那我相信面试官对他的评价会大大提升。
这里同样举一个简单的例子。例如做数据库实现,项目中有一个问题是数据库太大,不可能放到内存里,但如果都放硬盘的话又太慢,这是项目中的一个困难点。解决困难点的关键是同时利用内存的速度优势与硬盘的容量优势,设计一个存储分层模型。做实验观察到90%的针对数据库的查询仅集中在10%的数据上。那么解决方案可以是设计一个冷热分离的模型,仅仅在内存中存储一些热(即查询频繁)的数据,而将冷(即查询频率很低)的数据存在硬盘上,同时设定一定的策略定期做冷热数据替换。经过这样的设计之后,数据库的查询速度提升了30倍。
论文部分心得简要介绍自己research的背景与项目不同,很多冷门的research的背景面试官往往是不了解的,所以常常需要做相对详细一些的背景介绍。
模拟面试强烈建议在面试之前找人模拟一下,并让对方给你一些反馈。这样能够大大降低紧张感,熟悉面试流程并提高面试表现。当然了,还有一个重要的方式就是多多投递,先拿一些自己不target的公司练练手,磨练自己的心态与面试技巧。
面试大忌我也曾当过几次面试官,也参加过一些面试并了解过其他人的面试情况,这里简单说几条面试大忌,一定要避免犯的错误。
不懂装懂对自己不懂的东西(甚至是没有十成把握的东西),一定要诚实地说出来,千万不要不懂装懂。我把这一点放在最前面,是因为我作为面试官以及平时与人讨论技术的时候,就非常讨厌别人不懂装懂。面试官的水平往往比你高很多,一下子就能判断出来你是真懂还是装懂。所以,碰到自己不懂或者没把握的问题,我建议直接告诉面试官说这个问题我没把握,不是很懂。但如果你有一些思路的话,可以接着说“虽然我不太懂,但是可以试着说一下”,这就可以变成一个展示你解决问题分析问题能力的机会了。而如果你的分析思路很合理,得出的结论也大差不差,那甚至可以很大程度地提升面试官对你的评价。
狂傲不羁面试的时候,人要有自信,但是态度一定要平和并且尊重面试官,切不可恃才傲物、狂傲不羁。有一些公司会非常看重这一点,如果你给面试官留下了不好沟通的印象,那往往是一票否决。但面试的时候,偶尔也会碰到面试官不是很懂犯错误的情况(比如国内的一些大厂),这个时候你最好是平和地去与面试官讨论,如果他坚持不肯认错,那你也不要去较真,否则的话可能你面试就挂了。有一种情况是可以去与面试官较真的,那就是你完全不在乎这家公司的offer,这时候你可以放开了较真哈哈哈。另一方面,当你面试一家公司或者一个组,碰到面试官不懂装懂又不肯认错的时候,你也得考虑一下这个组是不是值得你去。
心态面试总会有运气成分与偶然性,放平心态,不要因为害怕被拒就不敢投递,也不要因为患得患失而在面试的时候十分紧张。在面试中尽量让自己自然、轻松。当然,一些轻微地紧张有时是可以让自己发挥更好的,但是要适度,切不可紧张过头。面试中即使有些内容答得不好,也不要当场就心态崩盘,要沉着应付。当自己没有什么思路的时候也不要太慌,可以试着从基本的地方开始分析。例如做算法题,可以分析一些toyexample,有时候能获得一些思路。回答CS基础题、systemdesign等题目也可以从基础的地方开始分析,甚至是与面试官一起一步一步得出结果。我自己在参加一次面试的时候,一道算法题问清楚题目就花了10多分钟,然后10多分钟没有思路,同时面试官还在给我施加一定的压力。要知道面试总共就45分钟,这样的表现属于非常糟糕的了。所幸我当时稳住了心态,利用一个toyexample得到了正确的思路,写出了bug-free的代码,最后还是让面试官相当满意。
最后需要说明的是,每个人都有自己的面试风格,很多面试官也会有自己的喜好,所以没有一套universal的面试方案。本文提到的一些技巧什么的,主要是我自己总结出来适用于自己的风格与方案,读者完全可以根据自己的实际情况与面试时候的感受来调整。举个例子,本文提到的编程部分心得,主要是针对Google这样的公司的算法题部分。我自己也有过一些面试经历,面试官非常不喜欢候选人在写代码的时候与他交流,甚至会在你写代码的时候自己去做别的事情:(这时候你最好就乖乖闭嘴,把代码写出来即可:)因此,也希望各位因时制宜,因地制宜,结合实际情况来进行面试。最后祝大家都能有满意的offer~
番外篇:找工作的流水账与心路历程本文的这一部分将以流水账的形式简单讲讲学CS这几年来的一些经历,以及找工作的流水账与心路历程。
一个CS领域的学习过程大致可以分为以下三个阶段。当然,有的时候不同阶段是可以迭代地进行,例如开始科研之后发现自己还缺少了某些基础知识,那可以再进行基础知识的学习。同时,在学习中,很重要的一个指导思想是要获得监督信息与正反馈。三个阶段如下:
这里再简单说一下如何找到合适的课程。我一般直接找MIT/Stanford/CMU/Berkeley(四大)的有视频的课程。MIT的课程很多在OCW有收录,可以在OCW里直接搜索。更常用的方式大概有两种,其一是在Google上搜学校名字+课程的名字找到课程主页,然后就可以跟这门课程了。例如想学操作系统,就搜MITOperatingSystem即可。第二种是在Youtube上搜学校名字+课程,很多有录像的课程可以这样搜到。除了这些名校的课程以外,很多MOOC的课程也是值得一看的。这里再简单给出我对各大院校/MOOC课程质量的评价:
主要的MOOC平台有Coursera,Edx和Udacity。Coursera算是三大mooc平台做得比较成功的了,课很多,有好课也有一般的课,鱼龙混杂。课程类型偏学院风。Edx我以前看的时候是平均质量最高的,但那时候完全不商业化,课很少,学院风。Udacity课也不错,挺多的质量挺高,课程是工业风。MOOC的好处是针对自学的人有优化,但坏处是课程难度普遍较低,适合零基础入门的时候用。四大院校的真实课程的难度与质量往往会高好几个档次。其中MIT的课程质量在我看来是最高的,公开的课程也多。Berkeley的也不错,但是公开的课程相对少。CMU的课程质量也还可以。Stanford的课程的讲课质量感觉可能会低一些>_<,因为很多是PhDTA上课,感觉比很多经验丰富的Professor还是有一些差距。如果实在找不到有视频的课程资源,那也只好不看视频,直接看课件、reading材料和写作业了。这样往往吃力一些,效果差一些,但也能学到不少东西。
CS学习历程这段非常冗长,可以直接跳过。
于是开始自学CS。因为周围没有认识学CS的人,就去看知乎上大家的推荐,大家都推荐从SICP和CLRS入门,于是我就去看SICP和CLRS并努力做习题,而且当时太年轻,看的还是翻译版的。看到后面怀疑自己完全就是弱智,别人的入门书我怎么就学不懂呢...其实有几个原因,一方面是这两门书挺难的,不见得适合入门;另一方面是翻译版翻译得也不是很好。可以说自己刚开始学CS的时候走了不少弯路。后来看到Coursera上有开Princeton的Algorithm课,就去把那个课跟了一遍,完成了习题。这时候才终于感觉原来自己还是能学会一些东西的:)又把Edx上MIT的6.001x跟完了,还看了一些Harvard的CS50课程的内容,这时候终于感觉自己算是开始学CS了。
需要说明的是,直到这个阶段,我对学习/CS/机器学习/做研究都还没真正的入门。现在回忆起来已经有点模糊了,但我大致记得当时的学习并不“主动”,纯粹是“被动”地理解书上写的内容、公式推导和代码等等。现在我认为真正的学习应该是非常“主动”地去进行,看书、论文的时候要对框架和motivation有一个清晰的把握,知道/理解算法这样设计的目的是什么,并要相对频繁地去asksomequestions,批判地进行阅读与消化。但可惜当时的自己还差得很远。另一方面,自己习题、coding做得太少,没有获得足够的监督信息,自以为自己学懂了,但其实只学了点皮毛。好在那时候还算是通过一些课程的作业获得了很多正反馈,支持着我的学习热情。
我一开始对能够进入浙大计算机学院是很有一些激动的心情的,也破天荒地去积极认识了很多同学,还认真制定了课程计划等等。但开学之后,我发现研究生的课程质量比我想象中差了很多。好在蔡登老师的机器学习课程让我感觉非常好,我认为蔡老师的授课水平和这门课的作业都是world-class的。可惜我运气不好,没能选上这门课,而且蔡老师怕TA压力太大,不给增加课程容量。我一开始就只能蹭课听,坐在最前面,学得很认真,课上蔡老师的问题也总是积极地第一个回答,最后竟然让蔡老师破例地为我签了条子,选上了这门课:)托这门课的福,我的机器学习基础扎实了很多,这门课也取得了不错的成绩。值得一提的是,这件事情也成了后来我成为蔡老师学生的契机。我还担任了这门课接下来的三任TA,在此期间我与另一位TA一起将之前的MATLAB作业改写成了Python的作业。后来这门课要扩充一倍的新内容,我还与蔡老师一起设计了新的课程内容,并狗尾续貂地再出了四份对应新内容的作业。只做了这么一点微小的工作,非常惭愧。
我在实习之前的学习经历就到此为止。前期因为没人指导,自己也没有很好地判断力,很愚蠢地相信了很多知乎上的答案,后来才慢慢有点上路了,总结出了一些适合自己的学习方案。这个学习方案在前问已经给出来了,此处不再赘述。
找工作之前的准备刷题我在找实习和找工作之前刷了一些LeetCode题目以做准备。找实习之前大约是刷了400道左右,到找工作之前大约是刷了700多道,具体可以移步我的知乎回答。不过由于我不是竞赛背景出身,不够有天赋,刷题也不是全都认真地独立完成而常常参考discussion,我直到最后也没能达到可以轻松做出绝大多数hard题目的水平(周围的很多朋友都可以轻松秒杀),这也给我面试一些对算法题要求很高的公司(例如hulu等)带来了一些不确定性。
模拟面试找实习之前幸运地获得了Google官方提供的模拟面试(mock)机会,通过mock直观地感受到了Google面试的形式与风格等,对后面拿到Google的实习offer有很大的帮助。后来在Google的实习的时候,公司很贴心地给每个实习生都安排了四次mock,这四次mock也让我学到了很多东西。除此以外就没有进行过mock了>_<
日常实习加入蔡老师的组之后,我一开始是在何老师的无人驾驶公司飞步实习。实习期间受宠若惊地担任了不少重任,例如一开始独自一人地做一个产品的某个算法模块(包括数据采集(与标注)、算法选型、模型训练调整、开发代码、测试、部署等)。值得一提的是,为了降低自己标注数据的负担(大概标了几万张分类图片),也自己想了一些合适的采集数据的方式,并设计了一些半自动标注的算法,大概能够降低95%的工作量吧;又开发了一些简单的标注工具,才终于让我survivethelabelingtask。后来还担任了某个产品的开发主力(甚至很多时候是唯一开发人员>_<),同样担任了数据收集、算法选型、模型训练调整、开发代码、测试、部署等等。在这段为期5个月的实习中我学/锻炼了非常多的能力,为我后面找工作也奠定了一些基础。
在飞步实习期间要特别感谢我的实验室同学Z。Z是我身边技术最强的朋友之一,无论是技术深度还是广度都是我生平罕见得强大。Z虽然并没有在公司实习,但在我实习期间他偶尔会来公司carry我一把。Z给我的帮助非常非常大,例如帮我一起解决了那个编译器的bug,带我一起完成了第二个项目的系统方案设计等等。后来Z也选择吃了我的安利,与我一起加入Optiver,成为浙大第三个应届拿到Optiveroffer的人>_<
这里再说一句题外话。我在实习期间因为要用C++,但自己以前用的基本上都是C++中的C,所以又找了些资料学了一些C++。特别推荐一个C++的学习资料,Stanford的C++课程CS106L的CourseReader。这本书讲的C++虽然有些过时,主要是C++11之前的一些东西,但仍然能够从里面学到非常多重要的C++思想(如封装、继承、抽象、多态、const的重要性等等)。对我来说,C++是一门不容易学好的语言,许多人推荐的C++Primer和C++PrimerPlus会将我淹没在语法细节的海洋中。但是这本CourseReader不一样,作者会从很多motivation/design的角度来讲述C++,并给出了很多motivationexample,况且作者的写作水平也很好,写得很有趣>_<如果你和我一样没什么学C++的天赋,发现其他的书学起来很困难,那么不妨试试这本。稍稍遗憾的是,这本书自10年之后就没有更新了。
应该说,我在硕士之前做research的路子是很有一些问题的,这个问题主要在于我的野心太大,常常想做一些以自己的能力/资源不大可能做出来的research,简直就像很多民科想证1+1=2或者P!=NP的人一样可笑。当时也没有人来指导自己的research,就拿一台i3小破笔记本自己一个人瞎想瞎折腾。后来有一位朋友和我说,他认为PhD需要培养两个重要的能力:其一是能够大致判断自己能否做出一项工作;另一点则是能够大致判断工作的impact。PhD应该在自己能够做出来的工作中,挑选impact最大的去做。而我当时显然就不具备判断自己能否做出某项工作的能力。
经历了一些失败的复现之后,向一位刚中了AAAI的小伙伴C请教了一些经验。C安利我做他们的领域,我听了他们的工作之后,当天晚上突然想了一个比较偏datamining的idea。第二天就写了代码实现一下,发现效果特别好。当时非常激动,觉得随便一弄效果就这么好,这要是认真调调岂不是要上天,结果那天的结果就基本上是最后调出来最好的结果了>_<然后匆匆写了一篇paper,submit到了IJCAI上。结果被reject掉了。AC评价说觉得是一个简单但是有效的工作,但是漏引了两篇10多年前idea相似的paper,我一看才知道原来10多年前就有人做过了相似的工作了,被拒得挺没有脾气的。后来这篇paper又submit到了AAAI上,有一位reviewer给了非常低的分,理由是认为datamining这类approach早就out-dated了。我后来也觉得这篇paper确实很难投中,就扔掉不管了。总得来说算是一段有些失败的research经验吧。
除了自己做的research工作以外,我还与导师一起讨论review了很多paper,注意到有些会议里的review真是招呼满天飞,不禁感到有些失望。就开始思考自己究竟是否是真正想做research的人。最后得出结论,我想做的是真正实用的顶级的research,但我显然远没有那个能力,只得作罢。硕士期间的research经历基本就到此为止了。
找实习先写一些简单的总结体会吧:
Google流程:两轮电面,主要是问算法题。
拼多多流程:笔试+HR面+两轮onsite技术面
摩根士丹利流程:全英文,笔试+电面+两轮onsite技术面
头条流程:笔试(内推可免)+3轮视频面+HR面
阿里流程:笔试(内推可免)+3轮电面+1轮交叉面+HR面
腾讯流程:笔试(内推可免)+3轮电面+HR面
微软流程:笔试(内推可免)+3轮onsite面
过程:找学长内推了微软苏州的SDE,与妹子一起去苏州onsite。一面问了项目和一个算法题,又问了两个设计模式的题。面试官给我的印象非常差,看不懂RangeFor就challenge我,没用过priority_queue也challenge我。二面的面试官比较nice,先问了项目。之后问了一道比较简单的算法题,然后是难一些follow-up,都相对轻松地解决了。最后还剩下10分钟,面试官就开始和我闲聊,问了我已经拿的offer,并问我有没有肉翻的打算,我说至少要等妹子也有肉翻能力了再一起吧,暂时不考虑。三面面试官是一个leader,先问了一道systemdesign,并讨论了一些面向对象的设计思想,然后问了我开放性的题目,比较难。总得来说,除了一面面试官给我印象非常差外,另外两个面试官都很nice,尤其三面的面试官水平感觉也很好。
Optiver流程:全英文,笔试+电面+两轮onsite面
过程:
终于要讲到自己最终选择的雇主Optiver了。我投递的是C++developer。之前提到,投递Optiver的主要原因是相信大神S学姐的眼光。在校期间听说了不少S学姐的传说,据说她读书期间写System代码就从来是bug-free的,秋招的时候腾讯问她“你开个价吧”,但照样被她拒绝。还听说她当时是拒了一家叫JaneStreet的公司的offer去了Optiver。不过S学姐的那些传说我也没有向她考证过。后来我才知道S学姐是浙大第一个应届进Optiver的人,而我则是第二个。
百度流程:笔试+三次技术面
Airbnb流程:笔试+笔试确认面+两次技术面+文化面
Hulu流程:笔试(内推可免)+三次技术面
至此,实习面试就全部结束了。总得来说各家公司的面试各有一定的特点,这可以通过看面经来了解。另一方面,这些面试也多多少少有一些共同点,考察的能力与一些相应的技巧我在本文前半部分也已经介绍过了,此处不再赘述。
实习经历Google的实习经历对我的影响、帮助都挺大,再加上Google可能是很多同学心目中的dreamcompany,这里就简单介绍一下我在Google的实习经历,以供参考。当然,其中也夹杂了许多我的个人经历,那些绝对是个例现象,与Google无关。
入职Google的入职活动(onboardingday)还是挺有意思的,上午是一位技术同事志愿服务地带我们逛了北京Office的各个地方,并给了一些介绍,然后给我们分了Laptop。然后HR小姐姐Y带我们参加了一个活动,是整个大中华区当天的入职的Googler(Google员工)一起开一个会,介绍Google的文化、基本信息等等,有一位非常Senior的领导会一起聊一聊。我记得当时应该是台北那边的一位VP参加了会议。印象最深的是提问环节,我看到没有人有问题,我就问了一个问题:“你对新入职的Googler有什么建议吗?”VP回答:“Trytoconfident.Yourproductsandeffortschangetheworld.”
之后与我的host见面聊了聊。他是一位非常nice的host,水平也很高,也很会带人,在实习期间教了我很多东西。host与我聊项目,是一个OCR的项目,我发现这个项目似乎和之前我得到的信息不太一样,就问他为什么变了。host告诉我说后来招了两个清华的本科EP实习生,打算让她们做之前安排我的项目。我还是比较flexible的,就愉快地接受了新项目。
这里顺带一提,在北京office的实习生们大多数都是清北的,而且很多同学都有本科甚至是初高中的竞赛背景。考虑到我的CS背景与他们的差距,这多多少少给了我一些peerpressure。又因为妹子在杭州实习,我周末就没什么安排,而我的房子租得又离公司非常近(步行5分钟),故我整个实习期间基本都是一周去7天公司,且在第一个月每天都是早上9点到晚上10点以后走的。顺带一提,Google确实相当注重work-lifebalance,我晚上的时候时常会闲逛一圈,极少看到有除了实习生之外的同事加班,周末则只能看到少数一些实习生。
顺带一提的是关于实习生转正的事情。由于妹子表示在阿里的组非常好,师兄们带她都很尽心,人也很好,晚上下班也挺早的,故我此时的秋招打算就是去阿里做推荐。当然Google的转正还是要争取一下,可以用来compete嘛。
进抢救室(下面将描述完全与CS无关的事情了,完全可以跳过>_<)
但是第二天有一场会议,我不想耽误他人的进度,故第二天起来挣扎地去了公司。路上我已经察觉到不对:我甚至已经没法正常走路了,我保持站立或者走路姿势大概两分钟左右就会脑供血不足,而不得不蹲一会儿让大脑供上血再继续前行。开完会后,我和host说明了情况,host表示非常担心,让我赶紧去医院检查一下,我就打了个的独自前往医院。实际上我对当时自己的危险情况并没有一个清楚的认识,正确的做法应该是找一位朋友一起,并拨打120去医院。
我又给host说明了一下情况,host非常担心,和Google的hr小姐姐们说了一下这个事情。hr小姐姐们也非常担心,赶紧问了一下我的情况,还有两位比较熟的小姐姐直接出发来公司看望了我,让我再次感受到了公司的温暖>_<我和她们笑着聊了会儿天,她们发现我还挺能说的,状态也不错。我说不用担心,没啥事的,就是个小意外,等我出院吧。
到了晚上C和妹子到了。后面的事情就省略说了。第二天做了胃镜,检查出来确实有两处严重的溃疡,高水准的医生用两个钛夹夹了一下。后来就转到肠胃科住院区去,并告诉了家里,我父亲之后来了北京。我住了一周的院,住院期间主要是妹子,我父亲和C陪护我,妹子主要负责照顾我的生活。期间还有一些朋友同学过来看我。巧合的是,我住院的主治医生是Google一位HR的闺蜜,定期向HR们汇报我的情况。经过了一周的治疗后我出院了。带妹子玩了两天之后送她回了杭州,然后休息了一天,就去公司学习去了。
恢复实习第一天去的晚上我host突然出现,和我说要不quit实习回家休养吧,还给我说了他Berkeley的导师给他说的一句话:“学业与事业是个橡胶球,挤一挤,变形了也总会恢复;身体与爱情是个玻璃球,挤一挤碎了就回不去了。”我解释说医生表示我现在其实状态已经相对正常了,只要不过分劳累、注意饮食、不要运动即可,是可以工作的。当时我心里有个很中二的想法,认为这一切的阻碍都是台阶,越是这样,我越是要迈过这些台阶,完成我的实习。另一个重要原因是我希望能用Google的offer来compete阿里的A+,因为我的profile直接面阿里应该是拿不到A+的。
后来公司的HR小姐姐问我要不要extend几周来补上住院的两周。我也直接拒绝了:我已迫不及待想回杭州了。答辩完,整理完所有项目的文档与代码,并与我的host以及两位co-host还有众多小伙伴告别后,我checkout了,并于第二天回到了杭州。Google的实习到此结束。
总结首先感谢我的诸位家人、朋友、同事、领导、师长等对我在这个阶段的支持与照顾,非常非常感谢。
秋招在实习部分已经总结过各家公司面试的特点了,这里就不再赘述了,只以简单的流水账形式说一下自己的秋招经历与心路历程。
阿里第三家的面试是阿里,同样是实习offer转秋招直通车,只需要一次面试。当时我内心的第一选择是秋招加入阿里,就很认真地准备了一下。面试官春招的时候已经面过我一次了,也没问什么特别的问题,只是特别问了我的意愿,我就照实说第一选择是阿里。面试官告诉我说等HR的面试吧。后来接了Optiver的offer之后我就拒掉了阿里了>_<
说实话,此时我还没有把Optiver放到内心的第一位置,当时的想法还是与妹子一起去阿里。不过我还是很担心自己能否承受得住阿里的工作强度,就与阿里的师兄交流了一下,他当时表示说应该还是可以的。
实习checkout之后,我迫不及待地回到了杭州。妹子向我吐槽了很久她暑假的一些没有告诉我的遭遇,并且说了一些她家里比较棘手的事情。说完她就和我提了分手,转身回去,我只能一脸懵逼地回宿舍,收拾心情准备第二天的面试。我想那些棘手的事情可能会需要比较多的钱,而之前就听说Optiver的待遇丰厚,那么这个offer一定要拿下来。于是第二天就晕乎乎地坐上了高铁,坐上高铁之后给妹子发了个消息,说等我回来我们再好好聊聊。
毫无疑问,Optiver的面试在我参加的所有面试中最具有特点。这并不完全是说面试的难度很高或者压力很大(当然难度也确实很高),还包括面试的形式、内容、考察的能力等等。通过面试,需要具有扎实的基础、严谨的思维、优秀的沟通能力、出色的抽象问题并解决问题的能力等等。更重要的是,在与面试官讨论设计方案时,我可以说是首次在面试中感受到了exciting的感觉。
过了两周左右,腾讯给我开了价格,让我稍微有些惊讶地开了个很高的价格。据我所知,国内的公司向来非常看重项目经历与岗位匹配度,因此我有点惊讶像我这样背景与岗位完全不匹配的候选人竟然能拿到这样的offer。看来腾讯的面试官们对我的潜力还挺看好的>_<不过我当时已经接了Optiver的offer了,因此也就直接拒绝了。不过让我有些没想到的是之后腾讯又联系了我好几次(4次以上?),非常有诚意,也感谢各位腾讯面试官的欣赏。其实这个offer也是一个挺不错的选择,工作内容有意思又核心,待遇也挺高,部门加班也不算多,如果没有Optiver的offer,我可能也会考虑这个offer的。
Google面完腾讯之后我又作死地去喝了一次酒>_<这次终于引起了相当严重的酒精过敏问题,让我一直发烧,连续几天都睡不了觉。喝完酒的第二天刚好是中秋节,回了趟家,并且因为不敢让父母知道自己喝酒的事情,没有去医院解决酒精过敏问题,只好终日在床上躺着。
Google的转正面试在中秋节结束后的第一天。很遗憾我在去上海前并没能成功地睡着。因此大概是72小时没睡着地去了上海。在上海的时候碰到了一起来面试的NLP大佬工友F,与F一块去吃饭的时候进行了一些关于textstyletransfer的学术讨论,感觉挺有意思。也许是因为与人讨论学术问题带来的愉悦感,也许只是因为酒精过敏终于好转了些,当天晚上终于成功地睡着了一会儿>_<
第二天到了Google的office进行面试。第一位面试官是毕业于Stanford的资深Googler,问了比较多的算法题。第二面是一位相对年轻一些的Googler,也考了两道算法题。虽然因为连续多天的缺乏睡眠而有些疲惫,但我还是顺利地把算法都写了出来。面完之后HR小姐姐问我想不想去国外的Office,我说我只考虑国内的Office>_<
Offer选择在与许多老师、朋友沟通后,我最终做出了选择Optiver上海的决定。这里还要特别感谢在Optiver的H学长,学长在收到我咨询的第二天竟然直接来了浙大与我面谈Orz至于我最终做出这个选择的理由此处就不说了。这里引用一位浙大学长求职经历帖的话:”如果你跟我足够熟的话,私下问我我会告诉你的。或者你就当我是选择了给钱给得多的offer就好了。“
这里简单谈谈我从算法岗转到开发岗的理由。这些理由在前文中也多多少少地提到了,此处也就不再赘述。其中最大的一个concern就是我对算法岗的工作内容有些不感兴趣。我担心自己对Optiver的datascientist岗位也不感兴趣,这才坚持选择了SystemDeveloper。不过公司在知道我的concern之后表示,我可以到公司之后看一看datascientist在做的事情,如果感兴趣的话也可以随时转岗,甚至两个岗位的项目一起做>_<另一方面,我也觉得自己的工程能力还是太弱了一些,也想借着从事SystemDeveloper的机会把自己的工程能力培养起来。
最后再简单谈谈为什么我会选择金融行业。这个问题的答案其实非常简单,因为Optiver是金融行业的公司>_<我选择了Optiver,所以也就这样选择了金融行业。
确认选择Optiver之后,我的秋招到此算是结束了。虽然我后面还面了几家公司,但主要也是奔着与面试官交流去的。
一些学习资料推荐这里简单推荐一些我看过的(有些看完了,有些看了部分)适合自学的学习资料,以供参考。因为我太懒了,所以覆盖的内容不全面,自学CS的同学也可以参考MIT的课表看看要学哪些内容。再次强调,上课不做题,等于白上课。
数学MIT18.01,MIT18.02:微积分。
MIT18.06:线性代数
HarvardStat110:概率论。印象深刻的有一句话,"Randomvariableisafunction"。
MIT6.042J:MathematicsforComputerScience.在我看来最有趣的数学课,如果TA能少上几节课就好了:(
CS导论MIT6.001:IntroductiontoComputerScienceandProgramminginPython
数据结构与算法MIT6.006:IntroductiontoAlgorithms,教材是CLRS。
MIT6.046:DesignandAnalysisofAlgorithms.进阶版,但其实前面那门课就已经内容不少了。
操作系统CMU15-213:教材是CSAPP。
MIT6.824:分布式系统。不久前终于感人地放出视频了。
MIT6.828:其实还没看,小伙伴都说好。
MIT6.004:ComputerStructure。偏硬件,老师讲得很好。
ModernOperatingSystem
编程语言StanfordCS41:PythonLanguageProgramming.可惜没有视频,作业挺有意思的。其实我觉得学语言比较好的一种方式就是学了语法之后做一些练手的项目、作业,这样会掌握得比较快。
StanfordCS106L:C++LanguageProgramming.前文也介绍过了,我觉得那本FullCourseReader写得真的很好,推荐一下~
还有一些领域内很知名的书,这里就不再专门推荐啦,这些课程里也会有介绍/推荐。这里再啰嗦一句,对大多数人来说,往往只需要学会某种编程语言中20%不到的常用特性,而这20%的常用特性往往占了实际使用这门编程语言中的90%。所以我不是很建议一开始弄本厚厚大大的书看,那样又吃力又缺少反馈,而且可能会花大量精力在自己根本用不到的地方。我其他的编程语言似乎都是用官方tutorial入门的,这里也就不推荐啦。
机器学习浙大的同学来蹭蔡登老师的课>_<
CourseraAndrewNG的MachineLearning:机器学习之路从这里开始。
StanfordCS229:MachineLearning.Youtube上有视频。
CMU15-701:IntrotoMachineLearning.就找到这么一年有视频的。
CourseraPGM:知名课程。
CMU15-708:ProbabilisticGraphicalModel我只看过两个Lecture。
下面有几本书,不过还是推荐和课程一起看。
PatternClassification(PC):比较老的书了,但是内容还是很有意义。
ElementsofStatisticalLearning:insight很多。
PatternRecognitionandMachineLearning(PRML)
MachineLearning:aProbabilisticApproach(MLAPP)
InformationTheory,Inference,andLearning:可惜DavidJ.C.MacKay英年早逝。
深度学习StanfordCS231n:DeeplearningforCV.推荐深度学习从这里开始
StanfordCS224n:DeeplearningforNLP.
BerkeleyCS285:DeepRL.我只挑着看了几个Lecture,小伙伴说好。
DeepLearningBook:花书。
尾声本来这篇的主要目的是总结一些自己的面试心得供大家参考,后来又加入了自己自学CS历程的介绍、心得等,又介绍了自己找工作的历程和思考,反而使得本文的后半部分显得有些喧宾夺主了>_<此时突然想到一位师兄博客里讨论的话题:“如果能回到十年前,你会对过去的自己说什么?”如果我能回到开始CS的旅程之前,我也许会对过去的自己说:“你会经历一段难以置信、跌宕起伏的旅程。你会遇到很多志同道合的好友,以及你十分尊敬的师长。虽然你也会走很多弯路,碰到很多困难与挫折,但是不要担心,你一直兢兢业业、勤勉刻苦,你的努力最终都会得到回报。”