本文是我在2013年找工作时给自己准备的面试纲要,此时以博客的形式写出来也算是对自己研究生生涯的一个小小总结吧。
面试准备
(1) 在纸上写代码;
(2) 熟悉你的简历:对参与的项目或者科研活动进行总结;阐述解决过得最困难的或者最有意义的问题;
(3) 切记,不要机械地记忆算法书上的解答,而是要理解算法实质;
(4) 面试官希望了解你思考和处理问题的方式,因此在解决问题时要大声说出来:让面试官了解你解决问题的过程,以便面试官进行有效的指导;
(5) 若事先知道将要面试的部门,最好提前了解这个部门的产品,相关技术,博客信息等;并能准备有建设性的建议,以此表现出对该部门工作的热情。
解决面试中技术问题的五个步骤:
(1) 询问面试官,消除对于问题的模糊理解;
例如:询问数据类型、数据量、解决问题所需的假设等。。。
(2) 设计算法;
例如:考虑算法时间空间复杂度、大数据量情况下算法的效率、多方面因素的平衡等。。。
(3) 编写伪代码,同时告知面试官你正在写伪代码,以免他认为你从不打算编写实际代码;
(4) 不要太快也不要太慢地编写实际代码;
注意:编码过程中使用数据结构、事先对代码进行布局,不要使代码都拥挤在一起等。。。
(5) 测试代码,发现并修正错误;
解决困难算法的五个常用方法
(1) 举例,给出具体例子,发现一般规律;
(2) 模式匹配,考虑该问题和什么问题类似,并从已知的相似问题的算法入手,通过修改已知算法,看能否解决该问题;
(3) 简化&泛化,通过改变问题的某个限制简化问题,然后尝试解决该简化问题,并从该简化问题的解决方案中考虑如何解决一般性的问题;
(4) 基准情况和扩展,从最基本的基准情况入手,增加元素数量,扩展问题规模;
(5) 数据结构头脑风暴,列举众多数据结构,并依此将其应用。。。。。。
常见问题及其回答
问题1:为什么选择加入我们公司?
我已经使用贵公司的产品很长时间了,并且我对贵公司能开发出如此出色的产品而惊讶,同时对于贵公司采用的解决方案很感兴趣。。。。。。
关键:表现出对该公司采用的技术很有激情!比如在处理大规模数据时采用的系统架构、比如处理的数据规模量级等技术。。。
问题2:你最大的缺点是什么?
有时我不太注重细节,虽然它使得我可以尽快完成所需工作,但是却会造成一些无意导致的错误,因此我总是需要一些朋友帮我检查下我的工作。。。。。。
关键:传递出自己真实的缺点,并强调如何克服这个缺点。而不要传递出不愿意承认缺点的傲慢态度。
面试结束后常见的提问:面试结束时如何向面试官提问题?
一般性问题:自己想了解的问题
(1) 一天之中大概有多少时间是在编程?
(2) 每周大概开几次会?
(3) 测试、开发、项目经理等职位之间的转换比率是多少?项目如何在这些职位间计划?
有见解的问题:表达自己的编程和技术问题
(1) 我注意到你使用X技术,但是如何用X技术解决问题Y呢?
(2) 为什么该产品选择使用X协议(架构、算法等)而不是Y协议(架构、算法等)?我知道在A、B、C情况下它有用,但是很多公司却因为D情况而不使用它
有激情的问题:能表现自己对技术有激情的问题
(1) 我对可扩展性很感兴趣,您在这方面是否有了解,或者我能通过何种途径进行学习?
(2) 我对X技术不太熟悉,但是我感觉它能够很好地解决Y问题,您能告诉我一些它的工作原理吗?
回答问题技巧
使用S.A.R技巧组织答案(Situation、Action、Response):首先描述情况,然后解释处理该问题所采取的行动,最后描述结果。例如,问题:在团队合作过程中遇到的最大挑战?
Situation:在我操作系统的项目中,我和另外三个成员一起合作,其中两个成员很优秀,但是另外一个对该项目并没有做出什么贡献。他在开会讨论问题时经常保持沉默,很少发言。在项目过程中,我们总是不得不去帮他完成他该做的工作。
Action:有次在项目遇到瓶颈的时候,我和他进行了一次谈话,在谈话过程中我发现他并不是懒惰,而是他对自己的工作感到迷惑,并且因为技术问题缺乏自信。我询问了他感觉在这个项目中可能擅长的部分,并和其他成员进行协调,将该部分的工作划分一部分交给他完成。
Result:尽管他依然是我们这个团队里面工作进展最慢的,但是我发现他在之后的讨论中也渐渐参与进来,并对自己的任务当前遇到的问题提出了建设性的建议。
简历中的项目或者科研描述模版:
通过实现Y功能完成了X任务,并最终导致了Z结果。。。。。。
例如:通过检查最常见的用户行为,以及运用Rabin-Karp算法,我设计了一个新的算法,在大概90%的情况下,可以将搜索的时间复杂度从O(n)降低到O(logn)。
简历中在“项目”一栏列出2到4个最重要的项目(包括科研项目),项目的描述中应该包括:使用的平台、采用的语言、运用的技术、项目类型、承担的主要责任等。。。
针对每个项目要做到以下的准备,准确填写相应的项目表格:
适用于基于MapReduce的项目:挑战+解决方案+学习
| 项目名称:基于mapreduce挖掘频繁模式——使用bitmap存储数据 |
在项目过程中遇到的最有挑战的事情
| 在使用MapReduce分布式框架改写数据挖掘算法——Aprori时,使用了七台相同配置的机器:其中一台作为NameNode(JobTracker),其他六台作为DataNode(TaskTracker)。当启动集群后,向HDFS上传实验数据时总会报错: could only be replicated to 0 nodes, instead of 1 当进入JobTracker页面时会发现Live Node为0。 我通过查看Hadoop官方文档以及查询stackoverflow上的相关问题,初步认定为集群在上传文件时还没有完全同步,HDFS拒绝上传文件。因此我只有等待集群同步(往往花费我十分钟左右的时间),即查看工作页面时发现Live Node不再是0时才上传文件。虽然解决了该问题,但是每次启动集群时都需要等待一定时间,有时候运气好时不需要等待,实质上并没有解决该问题! 在几天的查询资料和去开源社区上进行提问讨论后,我尝试着在Hadoop的conf文件夹的配置文件中配置一个Hadoop_tmp,并且使该文件和Hadoop文件处于同一个文件夹下面。在每次启动集群前(bin/Hadoop start-all),都通过脚本文件将Hadoop_tmp文件夹删除。这样当集群启动后就可以立即向HDFS上传文件,并不会再报could only be replicated to 0 nodes, instead of 1错误。 |
解决困难的过程中学到什么 | 我不仅解决了该问题,同时在社区上也回答了不少有同样问题的人的提问,最重要的收获是我通过和社区上的朋友交流,深刻地认识到导致该问题的原因可能是因为:HDFS日志在作怪,因此每次启动集群前都将日志文件清空,这样就可以不报错地上传文件。 Hadoop_tmp文件夹中存储两个文件夹,dfs和mapred,其中mapred文件夹下存储的都是日志文件,若希望进一步调试MapReduce程序,那么可以通过在程序中添加提示信息,这些提示信息都将在mapred/userlog/job_ _ /attempt_ m _ 和attempt_ r _中可知。 |
MapReduce配置信息如下所示:
core-site.xml配置信息: <?xml version="1.0"?> <?xml-stylesheet type="text/xsl" href="configuration.xsl"?> <!-- Put site-specific property overrides in this file. --> <configuration> <property> <!-- 参数(name)fs.default.name 值(value)NameNode的IP地址和端口--> <name>fs.default.name</name> <value>hdfs://localhost:9000</value> </property> <property> <name>hadoop.tmp.dir</name> <value>/home/hadoop/hadoop_tmp</value> </property> </configuration> |
hdfs-site.xml配置信息: <?xml version="1.0"?> <?xml-stylesheet type="text/xsl" href="configuration.xsl"?> <!-- Put site-specific property overrides in this file. --> <configuration> <property> <!--参数(name) dfs.name.dir 值(value)NameNode存储名字空间及汇报日志的位置 --> <!--参数(name) dfs.data.dir 值(value)DataNode存储数据块的位置 --> <name>dfs.replication</name> <value>1</value> <!--配置的备份默认为3,单机版的Hadoop设置为1 --> </property> </configuration> |
mapred-site.xml配置信息: <?xml version="1.0"?> <?xml-stylesheet type="text/xsl" href="configuration.xsl"?> <!-- Put site-specific property overrides in this file. --> <configuration> <property> <!--参数(name)mapreduce.jobtracker.address 值(value)JobTracker的IP地址及端口 --> <!--参数(name)mapreduce.jobtracker.system.dir 值(value)MapReduce在HDFS上存储文件的位置 --> <!--参数(name)mapreduce.cluster.local.dir 值(value)MapReduce的缓存数据存储在文件系统上的位置 --> <!--参数(name)mapred.tasktracker.{map|reduce}.tasks.maximum 值(value)每台TaskTracker所能运行的Map或Reduce的task最大数量 --> <!--参数(name)dfs.hosts/dfs.host.exclude 值(value)允许或禁止的DataNode列表 --> <!--参数(name)mapreduce.jobtracker.hosts.filename/ mapreduce.jobtracker.hosts.exclude.filename 值(value)允许或禁止的TaskTracker列表 --> <!--参数(name)mapreduce.cluster.job-authorization-enabled 值(value)布尔类型,标志着job存取控制列表是否支持对job的观察和修改 --> <name>mapred.job.tracker</name> <value>localhost:9001</value> </property> <!-- -Xmx2048m 为hadoop JVMs分配更多的堆空间 --> <property> <name>mapred.child.java.opts</name> <value>-Xmx2048m</value> </property> </configuration> |
基于可排序视图的RDF模式匹配
| 项目名称:RDF Pattern Matching using Sortable Views | |||||||||||||||||
项目简介 | 处理RDF数据查询方案:1.将三元组数据映射成关系数据库中的表结构,将查询转化为SQL语句(表的自连接操作,低效!); 查询:“父子是否曾经合著发表过论文?” 2.基于物化视图,采用图匹配技术(降低自连接操作次数) 基于物化视图技术处理模式重写,完成RDF查询模式匹配原理图: 其中 关键思想:通过存储的视图进行包含映射,覆盖查询模式Q的一部分子图,然后将查询模式的匹配结果可用视图覆盖的部分以及未覆盖的部分拼接而成! 注意:视图中的三元组<S,P,O>主语和宾语是变量,谓语是常量;而查询中的谓语是常量,主语和宾语允许是常量。 关键步骤:视图→查询模式子图的映射
| |||||||||||||||||
在项目过程中遇到的最有挑战的事情 | 挑战1:给定视图库 和查询模式Q,在查询Q和视图库中寻找包含映射是NP-hard,因为发现包含映射问题等价于图匹配问题以及子图同构问题。 解决方案:提出一种具有可排序性质的物化视图,使得视图和查询间的匹配代价仅和数据的输入规模线性相关。 可排序规则:
变量可以等价于任何东西! 模式序列化:固定视图序列(因为视图序列中仅有谓词是常量,易于排序),利用视图序列唯一确定查询模式序列。
挑战2:针对小规模数据集时倒排索引技术发现候选视图效果极好,但当可排序视图集 中包含大量的待选择的视图时,则需要针对每一个视图遍历扫描一遍倒排索引表,对匹配数目进行计数,判断是否可以选择该视图。在海量可排序视图集 中基于倒排索引技术发现可排序视图即耗时又低效。
| |||||||||||||||||
解决困难的过程中学到什么 | 基于倒排索引在视图集中发现可排序视图: 基本思路:(1) 首先将视图集 中所有视图的三元组序列进行格式化操作(变量统一用?v替换),统一格式后插入倒排索引中,并删除冗余三元组(即谓词相同的三元组序列,只留下一个);(2)将查询模式中的三元组模式进行扩展(用唯一变量?v代替常量,同时保证每个三元组模式经扩展后至少包含一个常量);(3)将扩展后的三元组和倒排索引中的三元组进行匹配,并设置计数器记录匹配数目。倒排索引中的#id(Vi)表示视图Vi中的三元组可以和扩展后的三元组中的某一个三元组匹配。 学习:给定可排序视图集 和查询模式Q,对于视图集 中的任何一个视图Vi,视图Vi到查询模式Q存在一个包含映射当且仅当倒排索引满足:sum(#id(Vi))=| Vi|(视图中包含的三元组个数) | |||||||||||||||||
| 项目名称:基于Twitter Storm平台并行挖掘最稠密子图 | |||||||||||||||||
项目简介 | 在带标签的无向图中,用户提供查询标签集,系统返回符合查询标签集约束的最稠密子图(稠密度定义:稠密部分所包含的边的总数除以顶点的总数),如下所示:黑色部分是最稠密子图,查询标签集={朋友,同事},稠密度=7/5=1.4
| |||||||||||||||||
在项目过程中遇到的最有挑战的事情 | 已有的基于最大流技术发现最稠密子图的方法需要O(logn)次迭代计算,但是不适合大规模数据集;2-近似算法通过每次删除度数最小的顶点,并比较删除前后稠密度的变化,来发现最稠密子图,但是一次仅删除一个顶点,效率较低! 挑战:针对大规模数据集,设计近似算法,并通过增加近似算法的近似因子,来加快算法的执行效率? 解决方案:不是一次仅删除一个顶点,而是一次删除一批满足条件的顶点!
| |||||||||||||||||
解决困难的过程中学到什么 | 学习1:DSFLC算法的解是最优解的2(1+ε)近似解。 假设顶点子集opt_V为给定标签集QLS约束条件下最稠密子图发现问题的最优解,由稠密度定义知,若opt_V为最优解,则删除最优解中的任何一个顶点,其稠密度值下降。因此最优解中的任何一个顶点的度数均高于平均度数,即∀i∈opt_V, deg(opt_V(i))≥Den(opt_V)。在算法执行过程中,当opt_V中的顶点i第一次被删除时,i一定也同时属于TempVS。由于opt_V V,因此存在deg(opt_V(i))≤2(1+ε)*Den(V),根据不等式的关系:Den(opt_V)≤2(1+ε)*Den(V),易知Den(V)≥Den(opt_V)/2(1+ε)。 学习2:DSFLC算法执行O(log1+εn)轮后结束。 2︱E(V)︱=∑i∈TempVS deg(i)+ ∑i∈(V-TempVS) deg(i),由于∑i∈TempVSdeg(i)>0,因此2|E(V)| > ∑i∈(V-TempVS) deg(i),(V – TempVS集合中的顶点的度数deg(v) > 2(1 +ε)*Den(V),因为只有这类点才没有删除,顶点度数小于等于2(1 +ε)*Den(V)的点已经加入TempVS集合)所以: 2|E(V)| > (2(1+ε) (︱V︱-︱TempVS︱) *︱E(V)︱/︱V︱)。化简上式可知:1/(1+ε) >(︱V︱-︱TempVS︱) /︱V︱,即 (1+ε) <︱V︱/(︱V︱-︱TempVS︱),存在 ︱TempVS︱>ε* (︱V︱/(1+ε))。 V的规模每轮至少减少1/(1+ε),易知经过O(log1+εN)轮算法结束。 学习3:预处理阶段的Storm逻辑拓扑图 其中DispatcherSpout从抽取的合作关系数据集中读取数据,然后转发给DeleteBolt,DeleteBolt删除不包含在QLS的边。由于这个操作是琐碎的,而且是耗时的,采用Storm流式处理框架就可以显现出Storm的优越性。MergeBolt搜集符合条件的数据,然后执行算法,并返回实验结果。 总结:对于任何参数ε(ε>0),DSFLC算法只需要扫描大规模数据集O(log1+εN)次,便可以获得最稠密子图问题最优解的2(1+ε)近似解 |