算法面试 / 04
排序链路与系统设计题
排序系统设计是推荐、搜索、广告面试的高频大题。面试官不是只想听模型结构,而是想看你能不能设计一条可上线的排序链路。
标准链路
常见排序链路:
候选生成 -> 召回 -> 粗排 -> 精排 -> 重排 -> 过滤 -> 展示 -> 反馈
每一层解决的问题不同。
召回
召回负责从海量物料中找候选。常见召回包括:
- 协同过滤召回。
- 向量召回。
- 热门召回。
- 类目召回。
- 关注 / 社交召回。
- 搜索 query 召回。
- 规则召回。
召回关注覆盖率、相关性、延迟和多样性。召回漏掉的东西,后面排序再强也没用。
粗排
粗排用于在较大的候选集中快速筛出较优物料。它需要比召回更准,但比精排更快。
粗排模型通常特征少、结构轻,目标是低延迟下保留精排可能喜欢的候选。
面试可强调:
- 粗排不是精排的简化复制。
- 粗排要优化 top 候选保留率。
- 粗排和精排之间要避免目标不一致。
精排
精排使用更丰富的用户、物料、上下文、交叉和序列特征,预测点击、转化、停留、互动等目标。
常见模型:
- LR / GBDT。
- Wide & Deep。
- DeepFM。
- DIN / DIEN。
- DCN。
- Transformer 序列模型。
面试时不要只背结构,要讲为什么选它:是否需要特征交叉,是否需要行为序列,是否需要多目标。
重排
重排负责处理精排分数之外的业务约束:
- 多样性。
- 新颖性。
- 频控。
- 去重。
- 内容安全。
- 商业目标。
- 生态平衡。
很多线上收益来自重排策略,而不是模型本身。
特征服务
系统设计题一定要讲特征。
特征分三类:
- 离线特征:历史统计、长期画像。
- 实时特征:最近点击、当前 session、实时位置。
- 上下文特征:时间、地点、设备、流量入口。
要关注训练和线上一致性、特征延迟、缺失值、版本管理和监控。
多目标排序
业务排序很少只优化点击。常见目标包括点击、转化、停留、GMV、投诉、留存、广告收入。
多目标处理方式:
- 多任务学习。
- 加权融合。
- 分层排序。
- 约束优化。
- 重排阶段做规则控制。
回答时要说清主目标和护栏指标。
系统设计回答框架
遇到“设计一个推荐系统”:
- 明确业务场景:内容、电商、广告、搜索。
- 明确目标指标:点击、转化、GMV、留存。
- 设计召回:多路召回和候选覆盖。
- 设计排序:粗排、精排、多目标。
- 设计重排:多样性、规则、生态。
- 设计数据闭环:日志、样本、特征、训练。
- 设计实验监控:A/B、延迟、分布漂移。
高频追问
召回怎么评估?
看 recall@K、覆盖率、命中精排 top 的比例、多样性、延迟和不同人群覆盖。不能只看整体召回率。
精排 AUC 涨了,线上不涨怎么办?
看 top 区间变化、校准、策略覆盖、特征一致性、样本偏差、多目标冲突和实验分层。
如何做冷启动?
新用户用上下文、热门、地域、入口和探索;新物料用内容特征、作者特征、相似物料、少量探索流量和质量预估。
如何做多样性?
可以在重排阶段加类目、作者、主题、价格带、内容类型等约束,也可以用 MMR、DPP 或业务规则。
一段标准回答
我会把排序系统拆成召回、粗排、精排和重排。召回保证覆盖和多样性,粗排在低延迟下筛候选,精排用丰富特征做多目标预估,重排处理多样性、频控和业务约束。上线前看离线排序指标和关键分桶,线上通过 A/B 验证主指标和护栏指标,同时监控特征分布、模型分数和链路延迟。
调度与系统设计(物流场景)
Q:请建立一个骑手排班优化模型(数学形式),同时满足:覆盖预测订单量、骑手工时上限、区域均衡三个约束。如何求解?
来源:阿里巴巴 / 算法工程师 - 运筹 & 因果推断
普通回答:用整数规划建模,用 solver 求解。
更好的回答:
建模框架(整数规划):
- 决策变量:x[i,t,z] = 骑手 i 在时段 t 区域 z 是否上线(0/1)
- 目标:最小化排班成本或最大化覆盖率
- 约束:① ∑ capacity(x) ≥ demand_forecast[t,z](覆盖);② ∑_t x[i,t,z] ≤ max_hours(工时);③ 区域覆盖骑手数差异 ≤ ε(均衡)
求解方法:
- 小规模:精确求解(CBC / Gurobi / CPLEX)。
- 大规模:列生成法、拉格朗日松弛(将工时约束拉入目标函数)。
- 实时场景:将 IP 松弛为 LP + 启发式修复,或用强化学习替代部分求解。
工程挑战:需求预测不确定性(鲁棒优化 / 随机规划)、动态骑手池变化(滚动优化窗口)。
考察点:能否把业务需求抽象成完整的数学优化问题,并根据规模选择合适的求解策略。
Q:请解释旅行商问题(TSP)与骑手多订单路径规划(VRP)的关系,并说明即时配送场景下 VRP 有哪些特殊约束?
来源:阿里巴巴 / 算法工程师 - 运筹 & 因果推断
普通回答:VRP 是 TSP 的多车辆版本。
更好的回答:
VRP 是 TSP 的推广:TSP 是单辆车访问所有节点,VRP 是多辆车分配配送任务,TSP 是 VRP 的特殊情况(车辆数 = 1)。
即时配送的特殊约束:
- 时间窗(VRPTW):用户预计送达时间约束,骑手必须在窗口内到达。
- 动态订单:订单实时插入,需要在线算法而非离线批量求解。
- 取送同路(Pickup & Delivery):骑手要先去餐厅取餐再送达,顺序不可颠倒。
- 骑手容量和载荷:同时携带订单数上限。
- 不确定性:行驶时间随实时路况变化(随机 VRP)。
考察点:对经典运筹问题的理解,以及能否把理论模型映射到物流业务的真实约束中。
Q:如何设计一个骑手 ETA(预计送达时间)预估模型?请从特征工程、模型选型到线上服务全链路说明。
来源:阿里巴巴 / 算法工程师 - 运筹 & 因果推断
普通回答:用 LightGBM 预测送达时间。
更好的回答:
特征工程:路径距离(直线 / 导航距离)、实时路况、历史骑手速度画像、订单数量和重量、时段和天气、商家出餐时间预估、当前骑手位置。
模型选型:
- 基础:XGBoost / LightGBM(可解释、快速迭代)。
- 进阶:图神经网络(建模路网结构)+ Transformer(建模骑手历史轨迹序列)。
- 不确定性估计:分位数回归(输出 P50 / P80 / P90)、Conformal Prediction。
线上服务:模型离线训练 → ONNX / TorchScript 导出 → 特征实时拼接(Redis / Flink)→ 低延迟推理(< 50ms)→ 监控(预估偏差、分布漂移)。
难点:Train-Serving Skew(训练和线上特征一致性)、长尾场景(极端天气、节假日)。
考察点:全链路思维,不只是模型选型,还要说清特征、服务、监控和长尾处理。
Q:Transformer 的注意力机制时间复杂度是多少?在长序列骑手轨迹场景中如何优化?
来源:阿里巴巴 / 算法工程师 - 运筹 & 因果推断
普通回答:O(n²),可以用稀疏注意力。
更好的回答:
标准 Self-Attention 的时间和空间复杂度均为 O(n²),n 为序列长度。
优化方案:
- 稀疏注意力(Longformer / BigBird):局部窗口 + 全局 token,降至 O(n)。
- 线性注意力(Performer / Linear Transformer):核函数近似,O(n)。
- 分层聚合:将轨迹按时间段聚合为”段向量”,再做段间注意力。
- Flash Attention:IO-aware 算法,降低 GPU 显存访问,训练速度提升显著但不改变复杂度量级。
- 降采样:轨迹点按关键节点(停留点、转向点)稀疏化。
考察点:是否真正理解复杂度瓶颈的来源,以及能否针对具体业务场景选择合适的优化策略。
Q:解释 Policy Gradient 与 Q-Learning 的核心区别,各自适用什么场景?
来源:阿里巴巴 / 算法工程师 - 运筹 & 因果推断
普通回答:Q-Learning 学值函数,Policy Gradient 学策略。
更好的回答:
Q-Learning(值函数方法):学习 Q(s,a),取 argmax 得到策略。样本效率高,但动作空间大时 argmax 不可行,不适合连续动作空间。
| Policy Gradient(策略梯度):直接参数化策略 π(a | s),梯度上升最大化期望回报。天然支持连续和随机策略,但方差大,收敛慢。 |
Actor-Critic:融合两者,Actor 是策略网络,Critic 是价值函数,用 Critic 降低 Policy Gradient 方差。PPO / A3C 均属此类。
物流场景推荐:离散小动作空间(如 3 档补贴级别)→ DQN;连续出价或调度 → PPO;大规模组合动作 → 注意力 + 指针网络解码。
考察点:不只是背定义,而是能否根据物流决策场景的动作空间特点选择合适的方法。
Q:实现一个简化版骑手-订单匹配:给定 n 名空闲骑手位置和 m 个待配送订单位置(二维坐标),用最小权重二分图匹配找到总距离最小的分配方案。
来源:阿里巴巴 / 算法工程师 - 运筹 & 因果推断
普通回答:贪心选最近的骑手。
更好的回答:
from scipy.optimize import linear_sum_assignment
import numpy as np
riders = np.array([[0,0],[1,2],[3,1]])
orders = np.array([[2,2],[0,3],[3,3]])
cost = np.linalg.norm(
riders[:, np.newaxis, :] - orders[np.newaxis, :, :], axis=2
)
row_ind, col_ind = linear_sum_assignment(cost)
print("分配:", list(zip(row_ind, col_ind)))
print("总距离:", cost[row_ind, col_ind].sum())
linear_sum_assignment 是 O(n³) 匈牙利算法实现。当 n / m 达到万级时,需要近似算法、KD 树剪枝或贪心 + 局部搜索。
考察点:二分图匹配(匈牙利算法)的理解和工程实现,以及大规模场景下的扩展思路。
下一篇建议继续看: