大厂真题 / 华为
华为 9.4 笔试真题 - AI 岗
本场考试概述
考试时间:2026 年 9 月 4 日
考试岗位:AI 岗
证据边界:材料称本场有 20 道选择题(15 道单选、5 道多选)和 2 道编程题,但正文只能够恢复 10 道单选题。本文仅收录这 10 道,不补写缺失的 10 道;两道编程题均完整整理。
第 1 部分:可恢复单选题
1. 多任务模型共享主干
“共享主干 + 任务头”结构最直接的工程收益是什么?
- A. 保证所有任务精度同时提升
- B. 完全消除任务间冲突
- C. 减少重复参数和重复计算
- D. 每个任务拥有完全独立的特征空间
答案:C。共享特征提取网络可复用参数和前向计算,但不能保证消除负迁移。
2. 单位向量的欧氏距离与余弦相似度
单位向量 $u,v$ 的欧氏距离平方与余弦相似度关系是什么?
- A. $\lVert u-v\rVert_2^2=\cos^2\theta$
- B. $\lVert u-v\rVert_2^2=1-\cos\theta$
- C. $\lVert u-v\rVert_2^2=2-2\cos\theta$
- D. 不存在固定关系
答案:C。展开平方并使用 $\lVert u\rVert=\lVert v\rVert=1$ 即得结论。
3. Top-p 采样
Top-p 参数为 0.85,商品概率依次为 $A(0.4),B(0.3),C(0.2),D(0.1)$,商品 A 被采样的概率是多少(保留 3 位小数)?
- A. 0.400
- B. 0.571
- C. 0.471
- D. 0.444
答案:D。最小核集合为 ${A,B,C}$,归一化后 $P(A)=0.4/0.9\approx0.444$。
4. 一元线性回归
模型 $\hat y=2x+1$,当 $x=4$ 时预测值是多少?
- A. 10
- B. 9
- C. 8
- D. 7
答案:B。
5. GRPO 与 PPO
GRPO 相比 PPO 的典型优势是什么?
- A. 不需要采样
- B. 必须训练 critic
- C. 使用组内相对奖励代替 critic
- D. 完全离线训练
答案:C。它使用同一提示下多个输出的组内相对奖励估计优势,从而省去价值网络。
6. RAG 文本切分
文档有 4800 个中文字符,约 1 个中文字符对应 0.7 token。chunk 大小 1024 token、重叠 128 token,至少需要多少个 chunk?
- A. 6
- B. 3
- C. 4
- D. 5
答案:C。总长约 3360 token,步长为 896;块数为 $1+\lceil(3360-1024)/896\rceil=4$。
7. 集中趋势
下列哪个不是描述随机变量集中趋势的统计量?
- A. 数学期望
- B. 中位数
- C. 方差
- D. 众数
答案:C。方差描述离散程度。
8. PCA 与 LDA
下列说法最准确的是?
- A. PCA 最大化类间距离,LDA 最小化重构误差
- B. PCA 有监督,LDA 无监督
- C. PCA 最大化投影总方差;LDA 使类内聚集、类间分离
- D. 有标签时 LDA 必定优于 PCA
答案:C。PCA 不使用标签;LDA 使用标签并优化类间散度与类内散度之比。
9. 伯努利分布的 MLE
抛硬币 10 次出现 7 次正面,正面概率 $p$ 的极大似然估计是多少?
- A. 0.3
- B. 0.7
- C. 无法确定
- D. 0.5
答案:B。伯努利参数的 MLE 是样本中成功比例 $7/10$。
10. Lagrange 与 Newton 插值
给定相同的 $n+1$ 个互异数据点,下列描述正确的是?
- A. Newton 插值新增节点时要重算全部基函数
- B. Lagrange 插值的代数精度更高
- C. Lagrange 更适合动态增加节点
- D. 两种表示得到的插值多项式恒等
答案:D。次数不超过 $n$ 且通过全部节点的插值多项式唯一;Newton 形式更便于增量加入节点。
第 2 部分:编程题
第 1 题:时间序列数据清洗与特征评分
题目描述
给定 $n$ 条按时间戳严格递增的记录 $(t_i,v_i,q_i)$。质量标记中,0 表示有效、1 表示可疑、2 表示无效。依次执行:
- 插值:仅处理 $q_i=2$ 的记录,前继/后继锚点只认 $q=0$。两侧都有锚点 $(t_a,v_a),(t_b,v_b)$ 时令 \(v_i=v_a+(v_b-v_a)\frac{t_i-t_a}{t_b-t_a};\) 只有一侧时复制该侧值;两侧都没有则保留原值。
- 异常平滑:窗口大小 $w$ 为奇数。位置 $i$ 的窗口是序列范围与 $[i-(w-1)/2,i+(w-1)/2]$ 的交集。窗口均值、总体标准差都由步骤 1 的同一份快照计算;若 $\lvert v_i-\mu\rvert>k\sigma$,则把该点替换为 $\mu$。所有替换统一写入新序列。
- 标准化:对步骤 2 的序列计算总体均值 $\bar v$ 和总体标准差 $s$,令 $z_i=(v_i-\bar v)/s$;若 $s=0$,所有 $z_i=0$。
- 评分:质量权重 $p_i$ 在 $q_i=0,1,2$ 时分别为 $1,0.5,0.25$。令 \(w_i=\frac{p_i\lvert v_i\rvert}{\sum_jp_j\lvert v_j\rvert},\qquad S=\sum_iw_i\lvert z_i\rvert.\) 若分母为 0,则 $S=0$。输出 $S\times100$ 四舍五入后的整数,恰为 0.5 时向上取整。
输入描述
第一行输入 $n,w,k$,其中
\[1\le n\le10^4,\quad 1\le w\le n,\quad w\text{ 为奇数},\quad 1\le k\le10.\]接下来 $n$ 行输入 $t_i,v_i,q_i$,满足
\[1\le t_i\le10^9,\quad -10^9\le v_i\le10^9,\quad q_i\in\{0,1,2\},\]且 $t_i$ 严格递增,至少有一条 $q_i=0$ 的记录。
输出描述
输出最终评分乘 100 后按题意取整的整数。
样例
输入
6 3 2
1 10 0
2 15 2
3 20 0
4 18 0
5 100 0
6 12 0
输出
152
算法
插值用正反两次扫描得到最近有效锚点。异常检测若使用“平方和减均值平方”,当数据整体很大但波动很小时会发生灾难性消减。为兼顾 $n=10^4$ 与数值稳定性,使用两层分块:
- 每块维护值的总和、块均值与块内离差平方和 $M_2$;
- 查询窗口时,把完整块通过 Chan 合并公式加入统计量,窗口两端不足整块的元素逐个加入;
- 这样不再相减两个约 $10^{18}$ 的近似数,方差由中心化离差直接得到。
块长取约 $\sqrt n$,每个窗口访问 $O(\sqrt n)$ 个完整块与边缘元素,总复杂度 $O(n\sqrt n)$,在 $n=10^4$ 时可行。全局标准化同样用 Welford 在线算法计算稳定方差。
正确性证明
最近锚点扫描显然给出每个位置左右最近的 $q=0$ 记录,因此步骤 1 严格按题意插值。
对步骤 2,Welford 单点更新维护样本数、均值和离差平方和。Chan 公式精确描述两个不交集合统计量合并后的均值与离差平方和;每个窗口被拆成互不重叠的边缘单点和完整块,合并后覆盖且仅覆盖该窗口,因此得到窗口总体均值与方差。所有查询只读取插值后的 cleaned,结果另存,满足快照要求。
步骤 3 的 Welford 统计覆盖平滑后全部元素,故得到题定总体均值和标准差。步骤 4 逐项代入权重公式并按 floor(x+0.5) 取整。四步均正确,最终输出正确。
Python ACM 题解
import math
import sys
def merge_stats(a, b):
na, ma, m2a = a
nb, mb, m2b = b
if na == 0:
return b
if nb == 0:
return a
delta = mb - ma
n = na + nb
mean = ma + delta * nb / n
m2 = m2a + m2b + delta * delta * na * nb / n
return n, mean, m2
def add_value(stat, x):
n, mean, m2 = stat
n2 = n + 1
delta = x - mean
mean2 = mean + delta / n2
return n2, mean2, m2 + delta * (x - mean2)
def clean_values(ts, raw, quality):
n = len(raw)
previous = [-1] * n
following = [-1] * n
last = -1
for i in range(n):
previous[i] = last
if quality[i] == 0:
last = i
last = -1
for i in range(n - 1, -1, -1):
following[i] = last
if quality[i] == 0:
last = i
values = [float(x) for x in raw]
for i in range(n):
if quality[i] != 2:
continue
left, right = previous[i], following[i]
if left != -1 and right != -1:
ratio = (ts[i] - ts[left]) / (ts[right] - ts[left])
values[i] = raw[left] + (raw[right] - raw[left]) * ratio
elif left != -1:
values[i] = float(raw[left])
elif right != -1:
values[i] = float(raw[right])
return values
def smooth_values(values, window, threshold):
n = len(values)
block_size = max(1, math.isqrt(n))
block_stats = []
for start in range(0, n, block_size):
stat = (0, 0.0, 0.0)
for x in values[start:start + block_size]:
stat = add_value(stat, x)
block_stats.append(stat)
def range_stats(left, right):
stat = (0, 0.0, 0.0)
while left <= right and left % block_size:
stat = add_value(stat, values[left])
left += 1
while left + block_size - 1 <= right:
stat = merge_stats(stat, block_stats[left // block_size])
left += block_size
while left <= right:
stat = add_value(stat, values[left])
left += 1
return stat
half = (window - 1) // 2
result = values.copy()
for i, value in enumerate(values):
left = max(0, i - half)
right = min(n - 1, i + half)
count, mean, m2 = range_stats(left, right)
variance = max(0.0, m2 / count)
if abs(value - mean) > threshold * math.sqrt(variance):
result[i] = mean
return result
def score(values, quality):
stat = (0, 0.0, 0.0)
for x in values:
stat = add_value(stat, x)
n, mean, m2 = stat
standard_deviation = math.sqrt(max(0.0, m2 / n))
quality_weight = (1.0, 0.5, 0.25)
denominator = sum(quality_weight[q] * abs(x) for x, q in zip(values, quality))
if denominator == 0.0 or standard_deviation == 0.0:
return 0
total = 0.0
for x, q in zip(values, quality):
weight = quality_weight[q] * abs(x) / denominator
total += weight * abs((x - mean) / standard_deviation)
return math.floor(total * 100 + 0.5)
def solve():
data = list(map(int, sys.stdin.buffer.read().split()))
n, window, threshold = data[:3]
ts, raw, quality = [], [], []
pointer = 3
for _ in range(n):
t, value, q = data[pointer:pointer + 3]
pointer += 3
ts.append(t)
raw.append(value)
quality.append(q)
cleaned = clean_values(ts, raw, quality)
smoothed = smooth_values(cleaned, window, threshold)
print(score(smoothed, quality))
if __name__ == "__main__":
solve()
复杂度分析
时间复杂度:$O(n\sqrt n)$;插值、建块、标准化和评分为 $O(n)$,$n$ 个窗口查询各为 $O(\sqrt n)$。
空间复杂度:$O(n)$。
易错点
- 无效点只能使用原始质量为 0 的点作为插值锚点。
- 窗口统计必须全部读取步骤 1 的同一快照,不能边算边改。
- 标准差采用总体口径,分母是窗口长度或 $n$,不是减 1。
sum(x*x)/n - mean*mean对大基线、小波动数据会数值消减,简单截零不能修复误差。- Python
round使用银行家舍入;题目要求 0.5 向上,应使用floor(x + 0.5)。
第 2 题:模型训练任务资源分配
题目描述
有 $n$ 个训练任务,每个任务必须在两个方案中选择一个。方案 1 消耗资源 $c_1$、耗时 $t_1$;方案 2 消耗资源 $c_2$、耗时 $t_2$。总资源消耗不能超过 $C$。首先最小化总耗时;若总耗时相同,选择总资源消耗更少的方案。
输入描述
第一行输入 $n,C$,满足 $1\le n\le100$、$1\le C\le1000$。
接下来 $n$ 行,每行输入 $c_1,t_1,c_2,t_2$,各数均在 $[1,100]$。保证至少存在一个可行方案。
输出描述
输出三个整数:最小总耗时、对应的最小资源消耗、剩余资源 $C-\text{实际消耗}$。
样例
输入
2 15
5 6 8 3
7 5 9 2
输出
8 14 1
算法
令 dp[j] 表示处理完当前若干任务、资源恰好消耗 $j$ 时的最小总耗时。初始 dp[0]=0,其余不可达。每处理一个任务,新建数组,分别选择两个方案转移。全部任务完成后,按资源消耗从小到大扫描,取耗时最小的状态;扫描顺序自然完成耗时相同时资源最少的次级目标。
正确性证明
归纳处理任务数。初始状态准确描述零个任务。假设 dp[j] 已准确覆盖前 $i$ 个任务的所有选择,处理第 $i+1$ 个任务时,每个合法方案必须且只能选择方案 1 或方案 2;两类转移枚举全部可能且没有遗漏或重复。因此新数组准确记录前 $i+1$ 个任务在每个精确消耗下的最小耗时。归纳成立。
最终所有不超过 $C$ 的合法方案都位于某个状态中,取最小耗时即得到主目标;在同耗时状态中取最小下标即得到最小资源消耗,故输出正确。
Python ACM 题解
import sys
def solve():
input = sys.stdin.buffer.readline
n, capacity = map(int, input().split())
infinity = 10**30
dp = [infinity] * (capacity + 1)
dp[0] = 0
for _ in range(n):
c1, t1, c2, t2 = map(int, input().split())
next_dp = [infinity] * (capacity + 1)
for used, total_time in enumerate(dp):
if total_time == infinity:
continue
if used + c1 <= capacity:
next_dp[used + c1] = min(next_dp[used + c1], total_time + t1)
if used + c2 <= capacity:
next_dp[used + c2] = min(next_dp[used + c2], total_time + t2)
dp = next_dp
best_time = min(dp)
best_cost = next(cost for cost, time in enumerate(dp) if time == best_time)
print(best_time, best_cost, capacity - best_cost)
if __name__ == "__main__":
solve()
复杂度分析
时间复杂度:$O(nC)$。
空间复杂度:$O(C)$。
易错点
- 每个任务必须二选一,不能跳过。
- 新任务必须从上一层转移,不能原地更新导致同一任务被选择多次。
- 状态下标必须表示“恰好消耗”,否则无法处理耗时相同下的资源次级目标。
- 最终只考虑 $0\le j\le C$ 的可达状态。