ARTICLE DETAIL

资讯详情

深耕编程入门与网站建设的一线实战洞察。

第四范式秋招后端笔试题详解:算法、机器学习与系统设计核心考点

第四范式秋招后端笔试题详解:算法、机器学习与系统设计核心考点 最近后台收到不少读者留言都在问AI公司后端岗笔试到底考什么尤其点名让我聊聊第四范式的秋招题目。趁着周末把2020年这套后端研发笔试题重新翻出来捋了一遍感触挺多。这套题放在今天看依然有很强的参考价值它不考偏题怪题但每一道都在暗示你这个岗位要的不是只会写CRUD的人而是能理解模型怎么落地、特征怎么流动、线上服务怎么稳住的后端工程师。适合正在准备校招笔试的同学、想转AI方向的后端开发以及想了解AI平台类公司技术栈的从业者仔细琢磨。第四范式的业务核心是帮助企业做AI转型所以后端研发角色天然和普通互联网后端有区别。你写的每个服务都可能要承接模型推理、特征计算、实验分流这类机器学习场景。这套笔试题的底层逻辑很明确算法题考基本功机器学习题考工程化理解Java和系统设计题考线上实战能力三者缺一不可。1. 这套笔试题的定位与考察逻辑1.1 先弄明白第四范式的后端要做什么想答好这套题首先得理解第四范式后端岗位的特殊性。第四范式不是做App的也不是做电商平台的它的核心产品是AI平台和机器学习相关的企业级服务。这意味着后端工程师写的大部分代码不是简单的业务逻辑而是围绕模型全生命周期展开的特征工程阶段的特征存储与读取、训练阶段的数据管道与资源调度、推理阶段的在线服务与性能优化、实验阶段的流量切分与效果评估。这种背景下后端岗位对候选人的要求就变得立体了。一方面要具备扎实的Java基础、并发编程、JVM调优能力因为线上推理服务对延迟和稳定性极其敏感另一方面要对机器学习基础有概念不需要你会手写Transformer但至少得知道逻辑回归的损失函数、AUC是什么、特征怎么处理否则你根本没法跟算法团队沟通。这套笔试题的考察点分布恰好就沿着这两条主线展开。值得注意的是第四范式的笔试风格并不追求题目数量多而是每一题都留了足够的思考空间。有些题看起来是常规题但加了一个限制条件后难度就上来了。比如让你实现某个数据结构但不允许用现成的库或者给你一个机器学习场景让你用工程思维去设计方案。这种命题方式背后的潜台词是我不关心你背了多少题我关心你在面对真实问题时能不能拆解、能不能落地。1.2 题型分布和分值逻辑的推测虽然没有官方公布的完整真题原文但结合技术社区历年考生的回忆和同类AI公司命题规律来看这套卷子的结构大致可以分为四个板块。第一个板块是算法与数据结构通常占30%到40%的分数考察方式以代码题为主偶尔会有填空题。第二个板块是机器学习基础概念占比在20%到30%之间形式可能是选择题、简答题也可能是给一个场景让你写推导。第三个板块是Java基础和并发编程占比20%左右重点考察JVM内存模型、线程池、锁机制这些线上排障经常用到的知识。最后一个板块是数据库和系统设计占比剩下的一部分往往给一个贴近业务的场景题考察候选人的架构思维。这个分值分配透露出的信息量很大。算法占比高说明他们重视基本功机器学习占比说明业务属性强Java占比说明技术栈以Java为主系统设计占比说明招的是能干活的人。整套题下来死记硬背的东西很少能拉开差距的恰恰是那些需要结合场景去分析的题。我建议准备笔试的同学拿到卷子先花两分钟浏览一遍分值分布按照先易后难的原则答题不要在某一题上死磕太久。1.3 和普通互联网后端笔试的差异在哪和纯互联网公司后端笔试题对比第四范式的题目有几个明显的差异点。第一数学推导出现频率更高。普通互联网笔试大概率考察数组、链表、动态规划而第四范式的题目里会出现逻辑回归推导、损失函数求导这类内容。第二工程和算法结合更紧密。有些算法题会包装成机器学习场景比如让你设计一个特征存储结构或者实现一个模型版本切换的逻辑。第三关注点更偏向在线服务。不少题目会围绕高并发、缓存、限流做文章这和AI平台线上服务的特性是吻合的。说白了这套卷子考验的不是你背了多少知识点而是你能否站在一个AI平台后端研发的角度去思考问题。这也就意味着单纯刷LeetCode是不够的还需要补充机器学习工程化和高并发系统设计方面的知识。理解了这一点再去看具体题目思路就会清晰很多。2. 算法与数据结构题的解题思路拆解2.1 LRU缓存O(1)操作是基本要求LRULeast Recently Used缓存几乎是各大厂笔试的常客第四范式这套题里也没有缺席。这道题说难不难但想写对、写快、写得优雅还是有不少细节要注意的。核心要求是实现一个支持get和put操作的LRU缓存时间复杂度都必须是O(1)。为什么必须是O(1)因为线上推理服务中特征数据量可能达到千万级别如果每次访问都要遍历一遍来淘汰最近最少使用的项服务延迟会高到无法接受。LRU的经典实现方式是哈希表加双向链表哈希表负责O(1)的查找双向链表负责O(1)的删除和插入。具体来说每次get的时候把对应的节点移到链表头部每次put的时候如果键存在就更新值并移到头部如果不存在就插入头部如果容量满了就删除链表尾部的节点。很多人写这道题的时候容易在几个细节上翻车。第一个是双向链表的边界处理比如链表为空时头尾指针怎么维护删除唯一节点时怎么处理。第二个是更新已有键时如果没有先删除旧节点再插入新节点就会导致链表指针错乱。第三个是节点类的设计到底是单独定义Node类还是复用Map的Entry会影响代码的整洁度。我建议在笔试环境下先画一个简单的链表结构图再动笔写代码两个边界条件写清楚基本就能拿全分。public class LRUCache { private static class Node { int key; int value; Node prev; Node next; Node(int key, int value) { this.key key; this.value value; } } private final int capacity; private final Node dummyHead new Node(0, 0); private final Node dummyTail new Node(0, 0); private final MapInteger, Node map new HashMap(); public LRUCache(int capacity) { this.capacity capacity; dummyHead.next dummyTail; dummyTail.prev dummyHead; } public int get(int key) { if (!map.containsKey(key)) { return -1; } Node node map.get(key); moveToHead(node); return node.value; } public void put(int key, int value) { if (map.containsKey(key)) { Node node map.get(key); node.value value; moveToHead(node); return; } if (map.size() capacity) { Node tailNode dummyTail.prev; removeNode(tailNode); map.remove(tailNode.key); } Node node new Node(key, value); map.put(key, node); addToHead(node); } private void moveToHead(Node node) { removeNode(node); addToHead(node); } private void addToHead(Node node) { node.prev dummyHead; node.next dummyHead.next; dummyHead.next.prev node; dummyHead.next node; } private void removeNode(Node node) { node.prev.next node.next; node.next.prev node.prev; } }这个实现里面有几个细节值得展开说一下。第一使用了dummyHead和dummyTail哨兵节点这样就不用处理链表为空时的特殊情况代码会清爽很多。第二moveToHead这一步是复用了remove和add两个操作避免重复代码。第三put的时候先检查是否存在再检查容量顺序不能反否则可能出现先删除后才发现键已存在的情况导致错误删除。笔试时如果时间紧张优先保证逻辑正确再优化代码风格。2.2 TopK问题海量数据下的两类思路TopK问题在这套题里的出场率也很高而且常常会加一个限制条件数据量很大无法一次性加载到内存。这种题考察的核心是你会不会在资源受限的情况下选择合适的数据结构来解决问题。第一种思路是基于堆的解法。维护一个大小为K的小顶堆遍历数据如果当前元素大于堆顶就替换堆顶并调整堆。这种解法的时间复杂度是O(N log K)空间复杂度是O(K)。这个方案适合K相对较小、数据可以流式读取的场景。第二种思路是基于快速排序的partition操作每一轮把数组分成大于基准值和小于基准值的两部分根据K的位置决定去左边还是右边继续找。这种解法平均时间复杂度是O(N)但最坏情况会退化到O(N²)且需要数据能随机访问所以不适合真正的海量数据场景。如果面试官追问数据量大到连K个元素的内存都不够怎么办这就要提到分布式思路了。把数据分片部署到多台机器上每台机器求出局部TopK然后再把各机器的局部TopK汇总求全局TopK。这其实就是MapReduce思想的一个雏形。第四范式这种AI平台公司经常要处理海量样本数据这种分布式TopK思路他们面试官是很吃这一套的因为这直接对应了特征筛选和高频词统计的真实场景。2.3 概率题和边界条件容易被忽略的送分题除了纯算法题这套笔试题里还会穿插一些概率题和涉及边界条件的场景题。概率题考察的是你对随机性本质的理解常见的有抛硬币、抽奖、洗牌等变体。边界条件题则考察代码的健壮性比如数组为空、除数为零、数值溢出时程序能不能给出合理的结果。我印象比较深的一道题是关于洗牌算法的要求实现一个公平的随机打乱算法。很多人第一反应是用Collections.shuffle但笔试环境往往不允许用现成库需要自己实现Fisher-Yates洗牌算法。这个算法的核心是从后往前遍历每次随机选一个当前位置到末尾之间的位置进行交换这样能保证每个排列出现的概率相等。这道题翻车的点通常是随机数生成范围搞错导致某些排列永远不可能出现。对于这类题我给的建议是答案不仅要正确还要能解释为什么正确。比如洗牌算法为什么每个排列概率相等这就涉及概率论里的乘法原理。如果能在答案里补充这个推导过程面试官会觉得你不是背的答案而是真的理解了原理。同理边界条件题也要主动写出防御性判断这反映的是工程习惯。3. 机器学习基础题的工程化视角3.1 逻辑回归损失函数从Sigmoid到梯度下降逻辑回归Logistic Regression是机器学习笔试的高频考点第四范式这套题里基本不会缺席。问法通常有两种一是直接让你写出逻辑回归的损失函数并推导梯度二是结合具体场景问你怎么防止过拟合。无论哪一种核心都是你对Sigmoid函数、交叉熵损失、梯度下降这三者关系的理解。首先明确一点逻辑回归虽然名字里有回归但它解决的是分类问题。模型输出的不是一个具体数值而是样本属于某个类别的概率。Sigmoid函数的作用就是把线性回归的输出映射到0到1之间公式是y 1 / (1 exp(-z))其中z是线性组合wx b。然后我们通过最大似然估计把每个样本的预测概率和真实标签的关系表示出来取负对数后得到交叉熵损失函数。这个损失函数的好处是它是凸函数可以用梯度下降法稳定地找到全局最优解。但工程上用的往往不是纯粹的批量梯度下降而是随机梯度下降或者小批量梯度下降因为训练样本量太大每次全量计算梯度的成本太高。在推导梯度的时候有个技巧很实用最终得到的梯度形式恰好是(x * (sigmoid(z) - y))也就是说残差乘以特征向量。这个简洁的形式背后是Sigmoid函数的求导性质不少面试官会顺着这个点问你Sigmoid的优点这时候你可以提到它的导数可以用函数值本身表示计算效率很高。如果题目问你如何防止过拟合方向有两个一个是正则化L1和L2分别对应拉普拉斯先验和高斯先验L1还能产生稀疏解适合做特征选择另一个是早停法在验证集误差开始上升时停止训练。回答的时候最好结合实际场景说明比如在特征维度较高的场景优先考虑L1正则在特征之间相关性较强的场景L2更稳。3.2 AUC与排序指标不要只会背公式AUCArea Under the ROC Curve这套题里也会出现但第四范式的考法往往更工程化一些。比如不直接问AUC是什么而是给你一个样本预测结果表让你手算AUC或者问你在正负样本极度不平衡的情况下用AUC评估模型是否合适该用什么指标替代。AUC的本质是随机选择一个正样本和一个负样本正样本的预测值大于负样本的预测值的概率。所以AUC对排序能力敏感对绝对值不敏感这使得它在广告点击率预估、推荐排序这类场景中很常用。计算AUC的常见方法有两种一种是先按预测值排序然后根据正负样本的秩和计算公式是AUC (正样本秩和 - 正样本数 * (正样本数 1) / 2) / (正样本数 * 负样本数)另一种是绘制ROC曲线计算曲线下的面积实际工程中多用第一种因为不需要画图。需要重点提醒的是在正负样本比例严重失衡的时候AUC反映的是排序能力但它对正样本覆盖率不敏感。比如你有100万个负样本和100个正样本模型把前100个样本全部预测为负类AUC可能依然很高但这恰恰说明模型完全失效了。这时候应该关注Precision、Recall、F1尤其是RecallK这种面向业务场景的指标。第四范式业务中很多场景都涉及模型精准度比如风控场景漏掉一个坏客户可能造成巨大损失所以面试官问这类问题是想确认你有没有线上评估经验的直觉。3.3 特征处理与样本采样工程落地的隐性考点这套笔试题里有一部分内容不是直接考公式而是给你一个业务场景让你设计特征处理方案或者判断样本采样方式是否合理。这类题看似开放实则有明确的回答框架考察的是你对机器学习全流程的工程理解。样本采样方面常见的考点是正负样本不平衡。比如点击率预估场景中点击样本可能只有千分之一直接训练出来的模型会倾向于把所有样本都预测为负类。解决办法包括欠采样、过采样、SMOTE合成少数类样本以及调整损失函数中正负样本的权重。其中过采样的一个陷阱是容易过拟合因为重复样本太多所以实际工程中常用数据增强或生成式方法去扩充样本。特征处理方面高频考点是离散化和归一化。离散化的好处是能引入非线性、增强模型鲁棒性、方便特征交叉归一化的好处是加快梯度下降收敛速度。第二范式这类AI平台场景中特征的量纲可能差异很大比如年龄和收入放在一起如果不归一化数值大的特征会主导梯度更新。回答这类题时如果能补充一句“特征是存放在分布式特征存储里的离线训练和在线推理使用同一套特征口径”这就是在展示工程化思维是加分项。4. Java与系统设计题的答题策略4.1 线程池参数你真的会算吗Java相关的考点在这套题里主要集中在并发编程和JVM上。线程池是一个怎么绕都绕不开的点因为后端服务几乎无处不在使用线程池处理任务。常见的问法是线程池的核心参数有哪些它们分别代表什么含义如果让你设置一个IO密集型和CPU密集型任务的线程池参数你会怎么设置先说参数ThreadPoolExecutor有七个核心参数corePoolSize、maximumPoolSize、keepAliveTime、unit、workQueue、threadFactory、handler。它们的配合逻辑是当请求数小于核心线程数时创建新线程处理当请求数大于核心线程数且队列未满时放入队列等待当队列满且线程数小于最大线程数时创建新线程当队列满且线程数达到最大线程数时执行拒绝策略。关键是参数怎么算。对于CPU密集型任务线程数一般设置为CPU核心数加一目的是稍微留一点余量处理偶发的页缺失或其他阻塞。对于IO密集型任务线程数可以设置为核心数乘以2因为IO操作会让线程阻塞等待这时候CPU是空闲的可以多开线程去处理其他任务。更精细的公式是线程数 CPU核心数 * (1 等待时间 / 计算时间)。当然这个公式里的等待时间和计算时间不是精确值是个估算。答题时如果能把这个公式写出来并解释为什么IO密集型能开更多线程面试官就会觉得你是理解原理而不是背答案。JVM方面的高频点是内存模型和GC。第四范式这种AI平台公司线上推理服务通常会面临大量对象创建和销毁GC调优是日常操作。笔试里常见的问法是JVM堆内存分为哪几块对象在什么情况下会进入老年代常见的垃圾收集器有什么区别。回答这些问题的关键是清晰表达对象生命周期和分区之间的联动关系而不是光背名词。4.2 SQL与索引避免select *只是底线数据库部分在整套题里占的比重不算特别大但出现频率不低主要是以SQL写法和索引优化为主。比较典型的是给了两张表让你写一个查询或者给一个慢SQL让你分析原因并优化。索引优化方面最容易被忽略的是联合索引的最左前缀原则。很多候选人知道有联合索引这个概念但一遇到具体问题就忘了最左前缀。比如创建了联合索引(a, b, c)查询条件是b 1这个索引是不会生效的因为你跳过了最左列a。但如果查询条件是a 1 and b 2那索引就能用上。笔试时如果遇到这种题最好把索引失效的几种情况都列清楚包括没有遵循最左前缀、对索引列使用函数、隐式类型转换、like以通配符开头等。另一个高频考点是select *的坏处。不少候选人觉得这只是个规范问题写不写都行但面试官真正想听的是select *会回表获取所有列如果表字段很多、包含大字段比如Text或Blob网络传输和IO开销会成倍增加。而且在覆盖索引可用的情况下只查需要的列甚至能避免回表性能差距非常明显。答题时如果能从覆盖索引的角度展开就不只是背诵而是理解了原理。SQL写法方面建议把窗口函数练熟。ROW_NUMBER、RANK、SUM OVER这些函数在排名和累加场景中非常好用而且SQL代码写出来比用子查询更简洁、执行效率也更高。比如让你统计每个部门的薪资Top3用窗口函数几行就能搞定用嵌套子查询则容易出错。4.3 高并发场景设计限流与排行榜的套路系统设计题是整套卷子区分度最高的部分。第四范式业务中线上推理服务往往承载着高并发请求所以限流、缓存、排行榜这类话题出现的概率极高。掌握几个经典设计的套路对答好这类题非常有帮助。限流算法常见的有四种固定窗口、滑动窗口、漏桶和令牌桶。固定窗口的优点是实现简单但存在临界问题比如第一秒最后100ms和第二秒前100ms各来了大量请求加在一起可能超过限制但固定窗口分两次计数都判定为正常。滑动窗口通过细分时间片把临界问题降到最低。漏桶算法把请求以固定速率流出适合保护下游系统不被突发流量打垮。令牌桶算法允许一定程度的突发流量因为桶里可以预存令牌适合既要限流又要允许短时峰值的场景。实现方式上在单机环境下可以用Guava的RateLimiter或者自己写一个基于AtomicLong的计数限流但在分布式环境下就得上Redis。Redis的INCR和EXPIRE组合能实现一个非常简洁的固定窗口限流而用ZSet则可以实现滑动窗口限流因为ZSet里天然存储了时间戳可以方便地删除窗口之外的数据。如果问到排行榜设计那套路就更明确了用Redis的有序集合ZSet成员的score就是分数排序问题直接交给Redis。ZSet底层是跳表插入和删除的时间复杂度是O(log N)足以应对百万级别的用户量。答题的时候要注意结构化表达。先说自己选什么方案再说为什么选它最后说遇到什么情况需要换方案。比如“这个场景我选令牌桶因为业务允许秒杀场景下短时流量增大但整体平均速率受控如果用漏桶会强制所有请求按固定速率处理秒杀效果会受影响”。这种思考路径比直接给出一个方案更能体现设计能力。5. 踩过的坑与复盘建议5.1 时间分配笔试现场最容易犯的错每年都有考生反馈题都会做但时间不够用最后只能草草交卷。这个问题本质上是时间分配策略错了。根据这套题的题型分布我建议把时间按比例分配算法题占40%的时间机器学习基础占20%Java和数据库占20%系统设计占20%。拿到卷子后先花5分钟整体浏览一遍明确哪些题是必拿分的基础题哪些题可能需要思考然后按从易到难的顺序作答。一个很多人没意识到的坑是代码题的环境差异。笔试平台往往跟本地IDE不一样没有自动补全、没有语法高亮甚至连快捷键都不一样。平时刷题用惯了IntelliJ的自动补全到了笔试现场连Scanner怎么拼都要想一下这种状态很影响节奏。建议提前在牛客网、LeetCode的模拟环境里做几次限时练习熟悉不带智能提示的编码方式至少把常用数据结构的写法练到条件反射的程度。时间分配还要考虑一点不要把太多时间花在选择题上。有些选择题选项设置得很刁钻两个选项看起来都对其实是在考察细微的边界认知。如果卡住了果断选一个最合理的标记一下回头有时间再细想先去做大头的大题。毕竟一道代码题的分值往往顶得上好几道选择题性价比完全不成比例。5.2 踩过的坑边界条件、格式化与编译器复盘了几个朋友和网友的笔试经历有几个高频翻车点非常值得注意。第一个是边界条件。比如TopK问题里K大于数组长度怎么办LRU缓存容量传0怎么办链表为空时怎么处理。很多思路完全正确代码逻辑也顺畅但一跑测试用例就崩溃原因往往是边界条件没处理好。建议每写完一道代码题花30秒主动检查三种情况空输入、单元素输入、最大规模输入。第二个是代码风格。有些笔试平台是人工阅卷即使代码能跑通如果变量命名毫无意义、缩进混乱、没有注释也会被扣印象分。相反如果代码里体现了清晰的思路比如先用注释写下关键步骤再填充实现即使有小错误也可能通过部分用例。这就像写作文阅卷人看到条理清晰的答卷容忍度就会高一些。第三个是编译器的差异。有些环境里Java版本比较老不支持var关键字不支持一些新特性。笔试前最好确认平台的编译器版本写代码时尽量使用Java 8的经典语法比如显式的类型声明、传统的for循环这样兼容性最好。同样的逻辑在本地能编译但提交到平台报错的尴尬场面能避免就避免。5.3 备考建议针对AI后端岗位的复习路线如果目标是第四范式这类AI平台公司的后端岗位备考路线不能只按照普通后端来准备要额外增加几个维度的内容。第一个维度是机器学习基础。不需要刷完吴恩达的整套课程但至少要把逻辑回归、决策树、GBDT这几个基础模型的原理和损失函数搞清楚同时了解精确率、召回率、AUC、F1这些评估指标的计算和适用场景。建议动手实现一次逻辑回归的训练和预测代码这样对数据格式、特征处理这些工程细节会有直观感受。第二个维度是分布式系统。AI后端经常要跟分布式存储、消息队列、任务调度打交道Kafka、Redis、ZooKeeper这些组件的使用场景要熟悉。不要求你把源码读透但至少要能说明白为什么用Kafka而不是直接HTTP调用、Redis的持久化方式有哪些区别、分布式环境下一致性和可用性怎么权衡。第三个维度是Java并发和JVM。这是很多AI公司后端笔试的重头戏因为线上推理服务对延迟敏感线程池参数设置、锁的选用、GC日志分析都是日常排障技能。建议刷一遍AQS的原理、ConcurrentHashMap的实现细节、常见垃圾收集器的适用场景这些内容在牛客网和博客上都有大量整理关键是要理解而不是背。第四个维度是项目复盘。笔试和面试往往是一套流程笔试通过后紧接着就是几轮技术面。如果简历上写了某个项目一定要把项目里的技术难点和方案取舍想清楚。比如你在项目里用Redis做了缓存就要想清楚缓存穿透、缓存击穿、缓存雪崩分别怎么应对。尤其是AI相关的项目要能解释清楚模型怎么离线训练、怎么上线服务、效果怎么评估回溯这套链路在第四范式的面试里基本必问。结合起来说备考的核心思路是以Java后端基本功为底座向上叠加机器学习工程化和分布式系统设计能力。这正好也对应了这套笔试题的考察逻辑。如果你平时只刷算法题不看机器学习基础不思考高并发场景大概率会在系统设计题上卡住因为那道题考的已经不是代码能力而是工程判断力了。从我个人的实际体验来看笔试不是刷题数量的竞赛而是思维方式的对齐。第四范式这套题最值得琢磨的地方在于它始终在提醒你后端工程师不只是一个代码执行者更是一个系统里的决策者你需要理解业务目标、理解算法需求、理解资源边界在多方约束下拿出一个可以落地的方案。能把这种思维练成习惯通过笔试只是时间问题真正的收获是面对任何复杂系统时你都有了拆解和重构的底气。如果你正在准备这类岗位的笔试建议把本文提到的几个核心知识点画成一张思维导图按板块逐个击破再找几套模拟题练手稳扎稳打走完这个过程后续面试你会感谢现在的自己。
返回列表