大厂真题
华为AI岗 2026-09-09
内容边界:可恢复 10 道选择题和 2 道编程题。CDN 题的中心更新同代价、空簇规则未完整给出,本文明确列出参考约定,不能据此声称唯一标准解。
本场考试概述
考试时间:2026-9-9 考试岗位:AI岗 难度评级:中等偏难
考点分析:
- 选择题:材料声称 20 道,实际可恢复 10 道;未公开的另外 10 道不补写。
- 第一题 动态KV缓存稀疏优化:排序贪心(难度中等)
- 第二题 CDN分发服务器选址:k-medoids 迭代(难度困难)
建议策略:
- 选择题的重心压在数学上,线性代数一门就占了近三分之一,转置、可逆、线性相关、迭代法这几个定义点务必背熟,属于必拿分
- 第一题看着吓人,实际规则题面已经写死,把「全局排序 + 三级优先级」翻译成一个排序键就结束了,不要真的去逐轮挑最大值
- 第二题是流程照做题,算法骨架题面给全了,分数全落在五条规则有没有一条不落地实现,尤其是两处同分怎么断
选择题(可恢复 10 道)
单选题
1、假设 和
均为
的方阵。在矩阵运算中,下列等式恒成立的是:
A.
B.
C.
D.
答案:A
难度:简单
考点:数学—线性代数
解释:矩阵乘法满足结合律但不满足交换律, 只用到了结合律,因此对任意方阵恒成立。B 错在转置要反序,正确的是
,只有
与
相等时才凑巧成立。C 展开为
,要合并成
必须
,一般矩阵不可交换。D 展开为
,同样需要
才能消去中间两项。三个错误选项的病根是同一个:把实数的交换律搬到了矩阵上。
2、矩阵 的协方差矩阵维度是?
A.
B.
C.
D.
答案:A
难度:简单
考点:数学—线性代数
解释:把 看成
个样本、每个样本
维特征,协方差矩阵刻画的是特征与特征之间的相关性,第
个元素为特征
与特征
的协方差,因此阶数由特征数
决定。按定义
,维度为
。B 是把样本数当成了阶数,对应的
是样本间的 Gram 矩阵而非协方差矩阵;C、D 都不是方阵,而协方差矩阵必为对称方阵。
3、假设单头注意力模型有 层,隐藏层维度为
,当前的 Context 长度为
,采用 FP16 存储,单个 Token 的 KV Cache 显存占用公式为?
A. 字节
B.
字节
C.
字节
D.
字节
答案:B
难度:中等
考点:大模型—微调与推理部署
解释:单个 Token 在每一层都要缓存 和
两个向量,每个向量
个元素,故每层
个数;共
层得
个数;FP16 每个数
字节,总计
字节。A 漏掉了层数
,只算了一层。C 的系数
相当于每层缓存四个矩阵,多算了
和输出,而
用完即弃、不进 Cache。D 混淆了统计口径:题目问的是”单个 Token”的占用,乘上
得到的是整个 Context 的总占用,且它还漏了
、
的因子
。
4、逻辑回归使用交叉熵损失而非均方误差(MSE)的核心原因是:
A. 交叉熵损失只适用于二分类问题 B. 交叉熵损失的计算速度更快 C. MSE 无法处理概率值输出 D. MSE 用于逻辑回归时目标函数非凸,易陷入局部最优;交叉熵损失是凸函数
答案:D
难度:简单
考点:机器学习—逻辑回归
解释:把 Sigmoid 套进平方误差后, 关于
不是凸函数,梯度下降可能停在局部极小;而交叉熵与 Sigmoid 组合后关于
是凸的,但不一定严格凸;最优解未必唯一,线性可分时甚至可能不存在有限参数最优解。还有一个连带好处:交叉熵的梯度为
,Sigmoid 的导数被约掉了,预测越离谱梯度越大;MSE 的梯度带有
因子,在饱和区趋近于
,会导致学习极慢。A 错在交叉熵天然支持多分类(Softmax 交叉熵)。B 错在两者计算量相当,速度不是选择依据。C 错在 MSE 在数值上完全可以接受概率输出,问题出在优化性质而非能否计算。
5、向量组线性相关是指
A. 向量组中至少有一个零向量 B. 存在一组不全为零的系数使得线性组合为零向量 C. 任意向量都可由其余向量线性表示 D. 向量的个数大于维数
答案:B
难度:入门
考点:数学—线性代数
解释:这就是线性相关的定义:存在不全为零的 使
。A 是充分不必要条件,含零向量必线性相关,但
无零向量也相关。C 把”至少有一个”错写成”任意”,如
相关,但
无法由前两个表示。D 同样只是充分条件,
维空间中超过
个向量必相关,但两个共线向量在二维里个数不超过维数却依然相关。
6、在使用 K-Means 时,常用” 手肘法 “(Elbow Method)来确定最佳 K 值。该方法观察的是哪个指标随 K 值变化的趋势?
A. Davies-Bouldin 指数 B. 调整兰德指数(ARI) C. 误差平方和(SSE / Inertia) D. 轮廓系数 (Silhouette Score)
答案:C
难度:简单
考点:机器学习—聚类
解释:手肘法画的是 SSE(簇内样本到各自簇心的平方距离和,sklearn 里叫 Inertia)随 的下降曲线。
增大 SSE 必然单调下降,当
越过真实簇数后下降速度骤缓,曲线出现形如手肘的拐点,该拐点即取值。A 和 D 都是聚类效果的内部评价指标,选
时取极值而非看拐点,不叫手肘法。B 是需要真实标签的外部指标,而 K-Means 场景通常无标签可用。
7、注意力机制中, 与
的乘积维度是?
A.
B.
C.
D.
答案:A
难度:入门
考点:深度学习—Transformer
解释:批量矩阵乘法只在后两维做矩阵乘,第一维 作为批维度对齐后保留。后两维按
相乘,内维
相消,得
,故结果为
。这个矩阵正是注意力分数矩阵,第
项表示第
个 query 对第
个 key 的相关度,行数必须等于 query 个数
。B 把两维写反了,对应的是
。C 误把被消掉的内维
当成了结果维。D 是
自身的形状,是没做乘法的结果。
8、矩阵 ,其中
是下三角部分,
是对角部分,
是上三角部分。那么
的 Gauss-Seidel 迭代法的迭代矩阵为:
A.
B.
C.
D.
答案:D
难度:中等
考点:数学—线性代数
解释:Gauss-Seidel 的思想是算新分量时立刻用上本轮已更新的值,即把 和
一起划到左端隐式求解:
,两边左乘
得
,迭代矩阵即
。A 是 Jacobi 迭代的迭代矩阵,它只把
留在左端、整轮都用上一轮的旧值。B 缺负号且丢了
,不对应任何标准格式。C 把
与
的角色对调,那是按逆序更新(后向 Gauss-Seidel)的形式,与题目给定的下三角、上三角约定不符。
9、已知矩阵 ,则
的转置矩阵
是
A.
B.
C.
D.
答案:D
难度:入门
考点:数学—线性代数
解释:转置就是行列互换,,主对角线元素
和
不动,副对角线的
与
交换位置,得
。A 是把矩阵旋转
的结果。B 是原矩阵未做任何操作,只有对称矩阵才满足
。C 只换了副对角线上的一个位置,并非合法的转置。
10、在预训练过程中,关于评估(Evaluation)和训练终止判断,以下做法最合理的是?
A. 定期在多样化的下游任务(如 HellaSwag、ARC、代码补全)上评估零样本(Zero-shot)性能,综合判断模型能力饱和情况 B. 当验证集 Loss 开始上升时立即停止,以防止过拟合 C. 仅在训练结束时评估,以节省计算资源,中间评估会显著拖慢训练进度 D. 以训练 Loss 作为唯一指标,当 Loss 不再下降时立即停止训练
答案:A 难度:简单 考点:大模型—评测与工程治理 解释:预训练关心的是通用能力,而 Loss 只是代理指标,与下游表现并非严格同步,常见现象是 Loss 已趋平但下游任务分数仍在爬升。因此工业界用一组多样化基准定期做零样本评估,综合判断能力是否饱和。B 错在大规模预训练通常单轮遍历海量语料,几乎不出现传统意义的过拟合,验证 Loss 的短期抖动更可能来自数据分布切换或学习率调度,据此立即停机会误杀。C 错在没有中间评估就无法及时发现训练发散或数据污染,等训练结束再看等于把整轮算力压在一次赌注上;且评估开销相对训练极小。D 错在训练 Loss 受学习率调度影响很大,且它衡量的是拟合语料的程度而非通用能力,单指标终止会过早停在能力尚未饱和的位置。
第 1 题:动态KV缓存稀疏优化
题目描述
在大型语言模型(LLM)的实时推理场景中(如长文档生成、无限对话系统),KV 缓存会随序列增长消耗大量显存。为解决显存瓶颈,需要对 KV 缓存进行稀疏化处理:在全局显存预算约束下,为每层每个注意力头选择重要性较高的 token 保留其 KV 缓存,且每层可保留不同数量的 token。
约束要求如下:
- 必须保留当前正在生成的 token(位置索引
)的 KV 缓存。
- 其他剩余位置的 KV,按注意力分数从高到低全局排序,优先保留注意力分数高的 token。
- 当注意力分数相同时,按以下优先级选择:层索引
从低到高排序,头索引
从低到高排序,token 索引
从低到高排序。例如
与
,则优先保留
内的 token。
- 总保留 token 数不超过显存预算
,本题中显存预算保证每层每头至少保留
个 token。
核心解题步骤如下:
- 计算必须保留的 token 数
(即每层每头位置
各一个),从预算中减去必须保留的 token 数:
。
- 对除位置
之外的所有注意力分数进行全局从高到低排序,取注意力分数前
的数值,记录对应的层索引
、头索引
、token 索引
。
- 如遇到注意力分数相同且超出显存预算的情况,按照约束要求
的规则选择。
- 输出符合要求的每层每头最终保留的 token 索引
。
输入描述
第一行包含四个整数 ,其中
为层数、
为头数、
为序列长度、
为显存预算。
随后 行:每行包含
个浮点数(空格分隔),表示该层所有头的注意力分数,每头连续
个浮点数,按照头索引顺序排列,每个注意力分数满足
且最多保留
位小数。
最后一行:,表示当前生成位置索引。
输出描述
输出 行,每行表示一层,包含
个字符串,字符串之间用空格分隔。
每个字符串表示该层该头保留的 token 索引 ,索引按照升序排列,以逗号分隔。
样例1
输入
2 2 3 7
0.1 0.2 0.1 0.3 0.3 0.3
0.3 0.4 0.6 0.3 0.5 0.6
1
输出
1 0,1
1,2 1,2
样例解释
共 层
头,每层每头都必须保留位置
,占掉
个预算,剩余预算为
。
除位置 之外的候选按分数从高到低排列为:第
层第
头的位置
与第
层第
头的位置
分数同为
,随后是四个分数为
的候选,最后是两个分数为
的候选。
取前 个候选,得到第
层第
头的位置
、第
层第
头的位置
,以及四个
候选中层索引与头索引最小的第
层第
头的位置
。因此第
层两个头分别保留
与
,第
层两个头都保留
。
样例2
输入
2 1 3 3
0.5 0.1 0.5
0.5 0.2 0.4
1
输出
0,1
1
样例解释
必须保留的位置 占掉
个预算,剩余预算为
。
候选中分数为 的有三个,分别位于第
层第
头的位置
、第
层第
头的位置
、第
层第
头的位置
。按层索引、头索引、token 索引依次比较,第
层第
头的位置
排在最前,唯一的剩余预算给了它。
题解:排序贪心
思路分析
每层每个注意力头都有 个 token 的注意力分数,全局只允许保留
份 KV 缓存,其中每层每头当前生成位置
的那份必须保留。问每层每头最终留下哪些 token 索引。
这是一道规则已经写死、难点在读懂规则的排序贪心题:要看清剩余名额是跨层跨头统一竞争的,以及同分时那条三级优先级怎么落到比较器上。
算法实现
先看名额怎么分。位置 无条件保留,每层每头各占一个,这是刚性开销:
题面保证每层每头至少留 个,故
。
剩下的名额给谁,约束二写的是”全局排序”。全局二字是关键:它不是给每层各切一份预算,而是把 个候选丢进同一个池子排队,所以每层保留的数量才会互不相同。
朴素做法是每轮扫全表挑出当前最优,重复 轮,复杂度
;两个因子都可达
,乘起来约
次比较必然超时。这些轮次其实在反复求同一个序的前几名,一次全局排序就能一并算完。
于是把候选写成三元组 (
),排序键取
第一维取负把分数降序与索引升序统一成一次升序排序;分数相同时依次比 、
、
,正好是约束三的三级优先级。这个键构成全序,排序结果与答案都唯一。
算法实现上分三步:
第一步,给每层每头放入 ,并算出
。
第二步,把 的候选按上面的键升序排序,取前
个按各自的
归位。
第三步,每个头内部把索引升序排一次,同层的 个字符串用空格连接后逐行输出。
第三步不能省: 是最先塞进去的,某个头后来抢到比
小的索引时,不排就会输出
这种逆序。
复杂度分析
时间复杂度:。瓶颈是候选池排序;建池与归位各
、末尾每头再排一次合计
,量级都更低。候选数上限
,约
次比较,远在限时之内。
空间复杂度:,分数矩阵与候选池各占这个量级。
题解代码
import sys
input = sys.stdin.readline
def solve(L, H, N, M, scores, C):
"""在显存预算 M 之内,算出每层每头最终保留的 token 索引。"""
kept = [[[C] for _ in range(H)] for _ in range(L)]
remaining = M - L * H
cands = []
for l in range(L):
for h in range(H):
row = scores[l][h]
for t in range(N):
if t == C:
continue
cands.append((-row[t], l, h, t))
cands.sort()
for _, l, h, t in cands[:max(remaining, 0)]:
kept[l][h].append(t)
for l in range(L):
for h in range(H):
kept[l][h].sort()
return kept
L, H, N, M = map(int, input().split())
scores = []
for _ in range(L):
row = list(map(float, input().split()))
scores.append([row[h * N:(h + 1) * N] for h in range(H)])
C = int(input())
kept = solve(L, H, N, M, scores, C)
for l in range(L):
print(' '.join(','.join(map(str, kept[l][h])) for h in range(H)))
正确性说明
先保留所有强制位置是可行解的必要条件。余下候选的比较键是题面规定的全序,取其前 remaining 个恰好执行规则;最终只对各头索引重排,不改变被选集合。
易错点与边界
全局预算不是每头均分;强制位置不能在候选池再次选中;分数相同时依次比较层、头、位置。
第 2 题:CDN分发服务器选址
题目描述
CDN 分发服务器是内容运营平台的重要组成部分,按业务设计,用户一般从距离最近的 CDN 服务器中下载内容。CDN 选址需要综合考虑覆盖的用户数量、距离等,从而使全部用户离 CDN 服务器的总距离相对较短。
现有 个城市,已知各城市坐标和用户数,以及预算可支持的 CDN 数目
,请使用 kmeans 算法求解 CDN 的选址坐标和覆盖用户数量。
注意,请按照如下要求计算:
- 初始阶段默认地图中无 CDN。
- CDN 选址必须在城市内。
- 迭代初始 CDN 选址为前
个城市。
- 当 CDN 不再变化时停止迭代。
- 如果出现多个与用户距离最短的 CDN,则选择序号靠前的 CDN。
单个用户到 CDN 的距离计算公式为:
其中城市坐标为 ,CDN 坐标为
。
全部用户总距离为:
其中城市 的坐标是
,
是距离城市
距离最小的 CDN 的坐标,
是城市
的用户数量。
输入描述
第 行为参数
,代表地图内的城市数目,
为正整数。
第 行至第
行,每一行格式为
,其中
代表该城市横纵坐标,
代表该城市用户数,单位为万人,
均为
位小数的浮点数,取值范围
。
第 行为参数
,代表当前预算可支持的 CDN 数目,
为正整数。
输出描述
第 行至第
行,每一行格式为
,
代表该 CDN 的横纵坐标,
代表该 CDN 覆盖的用户数,单位为万人,
均为
位小数的浮点数。
CDN 的顺序按照 从小到大排序,若两城市
相同,则按照
从小到大排序。
样例1
输入
3
0.00,0.00,2.00
1.00,1.00,3.00
2.00,2.00,5.00
1
输出
1.00,1.00,10.00
样例解释
三个城市为一簇,簇内选择一个最佳城市,使簇内所有用户到该城市的距离总和最小。
样例2
输入
4
0.00,0.00,1.00
1.00,1.00,2.00
3.00,3.00,1.00
4.00,4.00,2.00
2
输出
1.00,1.00,3.00
4.00,4.00,3.00
样例解释
四个城市坐标分别为 。
循环开始时,初始 CDN 设在 和
两个点,通过迭代明确其余两点最近的 CDN 在
,四个点分裂为两个簇,簇内重新计算最佳 CDN。挪动 CDN 坐标后,再次重新分簇,直到平衡。最终 CDN 坐标、人员分组如上输出。
题解:k-medoids 迭代
思路分析
在 个城市里挑
个放 CDN,每个城市的用户都就近接入,按题面规定的迭代过程反复”分簇、挪址”直到 CDN 不再变化,输出最终各 CDN 的坐标与它覆盖的用户数。
这是一道流程照做题:算法骨架题面已经给全,难点不在想到什么,而在把五条规则一条不落地落进代码,尤其是两处同分怎么断。
算法实现
先看它与课本 kmeans 的差别。标准 kmeans 的簇心取簇内坐标均值,可以落在任意位置;本题第二条规则要求 CDN 必须建在城市里,所以挪址时只能从簇内已有的城市中挑一个,这就是 k-medoids。
挪址挑谁,由题面的 式决定。对簇
,选城市
的代价为
取 最小的那个
。距离必须按用户数
加权,漏掉权重在样例二就会对不上。
算法实现上分三步:
第一步,取前 个城市作为初始 CDN,序号
的 CDN 记作
。
第二步,分簇。每个城市扫一遍 个 CDN,归给距离最小的那个。
第三步,挪址。每个簇内枚举候选城市 ,取
最小者作为新的
。新旧
完全一致就停止,否则回到第二步。
两处同分都按”序号靠前”断:分簇时等距取 CDN 序号小的,挪址时代价相等取城市序号小的。实现上只要坚持”严格更优才更新”,先遍历到的那个自然胜出。
数值计算用 1e-9 容差处理样例中的理论同代价;这一容差是工程约定,并未证明所有合法输入的非零代价间隙都大于它。
精确计算下目标值不增,但仅凭“有限状态 + 单调不增”不能排除同值循环。实现按确定性规则更新,检测重复中心配置;若未达到固定点而出现循环,则显式报错,不把旧分簇当成收敛答案。浮点容差不构成数学上的全局收敛证明。
复杂度分析
时间复杂度:O(Tp²+q log q),T 为到固定点或重复配置前的轮数,不预设为常数。
空间复杂度:O(p+Tq),包含城市、分簇及用于循环检测的中心历史。
题解代码
import sys
input = sys.stdin.readline
from math import sqrt
EPS = 1e-9
def dist(a, b):
"""两座城市之间的欧氏距离,城市用 (x, y, u) 三元组表示。"""
return sqrt((a[0] - b[0]) ** 2 + (a[1] - b[1]) ** 2)
def assign(cs, centers):
"""分簇:每个城市归给距离最短的 CDN,同距时保留序号靠前的那个。"""
cluster = [[] for _ in centers]
for i in range(len(cs)):
best_j = 0
best_d = dist(cs[i], cs[centers[0]])
for j in range(1, len(centers)):
d = dist(cs[i], cs[centers[j]])
if d < best_d - EPS:
best_d = d
best_j = j
cluster[best_j].append(i)
return cluster
def relocate(cs, centers, cluster):
"""挪址:簇内换一个城市当 CDN,使簇内加权距离和最小。"""
nxt = list(centers)
for j in range(len(centers)):
mem = cluster[j]
if not mem:
continue
best_c = -1
best_cost = 0.0
for c in mem:
cost = 0.0
for i in mem:
cost += cs[i][2] * dist(cs[i], cs[c])
if best_c < 0 or cost < best_cost - EPS:
best_cost = cost
best_c = c
nxt[j] = best_c
return nxt
def solve(cs, q):
"""迭代到 CDN 不再变化,返回最终选址与对应的分簇。"""
centers = list(range(q))
cluster = [[] for _ in range(q)]
seen = set()
while tuple(centers) not in seen:
seen.add(tuple(centers))
cluster = assign(cs, centers)
nxt = relocate(cs, centers, cluster)
if nxt == centers:
return centers, cluster
centers = nxt
raise ValueError("CDN 中心出现循环,题面未规定此时的输出")
p = int(input())
cs = []
for _ in range(p):
a, b, c = input().split(',')
cs.append((float(a), float(b), float(c)))
q = int(input())
centers, cluster = solve(cs, q)
rows = []
for j in range(q):
cov = 0.0
for i in cluster[j]:
cov += cs[i][2]
rows.append((cs[centers[j]][0], cs[centers[j]][1], cov))
rows.sort(key=lambda r: (r[0], r[1]))
for r in rows:
print("%.2f,%.2f,%.2f" % r)
正确性说明
分配步骤逐城市选择最近中心,固定中心时最小化加权距离;更新步骤枚举簇内所有城市,固定分簇时得到最小加权距离的候选。该流程是局部迭代,不保证全局最优。本文的同代价和空簇规则属于明确列出的实现约定。
易错点与边界
题面称 kmeans,但要求城市内选址且优化欧氏距离加权和,本文按样例解释采用 weighted k-medoids。题面仅明确等距分配选择较早 CDN,没有完整规定中心更新同代价、空簇与数值容差:本文约定更新同代价取输入序号较早城市、空簇保持原址、使用 1e-9 容差。这是与给定样例一致的参考实现,不宣称唯一标准答案或全局最优。坐标约束写为正数,但样例含 0.00,程序兼容零坐标。
小结
优先从题面约束提炼模型,再用样例检查边界。本文保留题面数学符号的原始 SVG,代码统一为 Python 3;未给出的评测限制或规则不补作事实。