
华为校招实习机试在线OJ刷题系统 在线OJ注册使用教程目录题目思路Code题目题目内容在轻量化大模型推理引擎中Transformer 的隐藏状态会在多个子模块之间不断传递。为了减少内存读写、提升推理效率实际工程中常常会将多个操作融合到同一个算子中例如残差连接 Residual Add、RMSNorm 归一化、逐元素缩放 Scale、逐元素偏置 Bias、Padding Mask 处理。本题要求按照如下计算步骤实现一个简化版的 Masked Fused RMSNorm 模块。计算步骤对于第 $k$ 个输入样本向量 $x_k$给定残差向量 $r_k$、残差系数 $\alpha_k$、有效长度 $l_k$、缩放向量 $\gamma$、偏置向量 $\beta$、数值稳定项 $\epsilon$。1. 残差融合$h_{k,i} x_{k,i} \alpha_k \cdot r_{k,i}$2. 有效部分的 RMS 和 energy仅前 $l_k$ 个位置为有效位置后续位置视为 padding不参与 RMS 和 energy 计算。$energy_k \sum_{i1}^{l_k} h_{k,i}^2$$RMS_k \sqrt{energy_k / d_k \epsilon}$其中 $d_k \max(l_k, 1)$3. 归一化输出对于输出向量 $y_k$有效位置$1 \le i \le l_k$$y_{k,i} \gamma_i \cdot h_{k,i} / RMS_k \beta_i$无效位置$l_k i \le n$$y_{k,i} 0$任务说明给定 $m$ 个样本请对每个样本计算并输出Masked Fused RMSNorm 后的结果向量 $y_k$、该样本有效部分的 $RMS_k$、该样本有效部分的平方和 $energy_k$。输入格式第一行三个数 m、n、eps分别表示样本个数、每个向量的最大长度、RMSNorm 的数值稳定项第二行 n 个浮点数依次为缩放向量 $\gamma_1, \gamma_2, \dots, \gamma_n$第三行 n 个浮点数依次为偏置向量 $\beta_1, \beta_2, \dots, \beta_n$随后 m 行每行一个样本格式为l alpha x_1 x_2 … x_n r_1 r_2 … r_n其中 l 是当前样本的有效长度alpha 是残差系数之后依次是输入向量 x 的 n 个值和残差向量 r 的 n 个值。数据范围$1 \le m \le 200$$1 \le n \le 256$$0 \le l_k \le n$$0 \le \epsilon \le 1$$-10^8 \le x_{k,i}, r_{k,i} \le 10^8$$-10^3 \le \gamma_i, \beta_i \le 10^3$$-10^3 \le \alpha_k \le 10^3$。输出格式共 m 行每行依次是 n 个结果、一个 |、RMS、一个 |、energy。相邻数据之间只用一个空格| 两侧也各有一个空格。全部浮点数保留两位小数按标准四舍五入恰好满五时远离零绝对值小于 0.005 的数一律写成 0.00不要输出 -0.00。样例1输入2 3 0.01 1.0 0.5 -1.0 0.0 1.0 0.0 3 1.0 3.0 4.0 0.0 0.0 0.0 0.0 2 0.5 -1.0 1.0 -1.0 2.0 -2.0 1.0 0.0输出1.04 1.69 0.00 | 2.89 | 25.00 0.00 1.00 0.00 | 0.10 | 0.00思路整体思路按题面公式对每个样本做三步模拟——残差融合、只算有效位的 energy 和 RMS、再按 mask 输出归一化结果重点难点全在输出格式上标准四舍五入、两位小数、去负零。1. 读入 m、n、eps以及长度为 n 的缩放向量 gamma 和偏置向量 beta之后每个样本一行依次是有效长度 l、残差系数 alpha、输入向量 x 的 n 个数、残差向量 r 的 n 个数。2. 残差融合对每个位置算 h[i] x[i] alpha * r[i]得到融合后的隐藏状态。3. energy 只累加前 l 个有效位置的 h[i] 平方padding 位置一律不参与分母 d max(l, 1)l 为 0 时取 1 防止除零RMS sqrt(energy / d eps)。4. 输出向量i l 的 padding 位固定输出 0不能把 beta 加进去有效位输出 y[i] gamma[i] * h[i] / RMS beta[i]gamma 和 beta 按位置下标取不要误用样本编号。5. 特殊分支若 RMS 恰好为 0有效位全零且 eps 为 0此时 h[i]/RMS 没有定义但每个有效 h[i] 必然是 0归一化项按 0 处理有效位只保留 beta[i]。6. 格式每个数保留两位小数用标准四舍五入恰好满五时远离零。不要用 printf(%.2f)、Python 的 format 或 Java 的 String.format它们走银行家舍入会把 0.125、1.125 印成 0.12、1.12。做法是先放大 100 倍按分取整正数 floor(x*100 0.5)、负数 ceil(x*100 - 0.5)C/C 用 llround(x*100)Go 用 math.Round这样恰好满五时远离零。绝对值小于 0.005 的数一律写成 0.00这样不会出现 -0.00。同一行用单个空格分隔| 两侧各有一个空格输出形如 y1 y2 … yn | RMS | energy。复杂度每个样本只扫常数遍长度为 n 的向量总时间 O(m*n)额外空间 O(n)。在 m ≤ 200、n ≤ 256 的范围下线性扫描绰绰有余。关键坑点一是四舍五入必须满五远离零而不是银行家舍入二是 |x| 0.005 直接写 0.00 去掉负零三是 energy 可达 1e24 量级必须用 double 累加塞进 32 位整数会溢出四是 padding 位不能带 betal0 时整行都是 0 但 RMS 仍要按 eps 输出。CodeC#include bits/stdc.h using namespace std; // 把数四舍五入到两位小数恰好满五时远离零|x|0.005 一律 0.00 string fmt(double x) { if (fabs(x) 0.005) return 0.00; // llround 对 .5 一律远离零不走银行家舍入先放大 100 倍按分取整 long long cents llround(x * 100.0); bool neg cents 0; unsigned long long a neg ? (unsigned long long)(-cents) : (unsigned long long)cents; ostringstream oss; if (neg) oss -; oss a / 100 . setw(2) setfill(0) a % 100; return oss.str(); } int main() { ios::sync_with_stdio(false); cin.tie(nullptr); int m, n; double eps; cin m n eps; vectordouble g(n), b(n); for (int i 0; i n; i) cin g[i]; // 缩放向量 gamma按位置下标取 for (int i 0; i n; i) cin b[i]; // 偏置向量 beta按位置下标取 while (m--) { int l; double alpha; cin l alpha; vectordouble x(n), r(n); for (int i 0; i n; i) cin x[i]; for (int i 0; i n; i) cin r[i]; // 残差融合h x alpha * r vectordouble h(n); for (int i 0; i n; i) h[i] x[i] alpha * r[i]; // energy 只累加前 l 个有效位置的平方padding 位不算 double energy 0.0; for (int i 0; i l; i) energy h[i] * h[i]; // l 为 0 时分母取 1防止除零 int d l 0 ? l : 1; double rms sqrt(energy / d eps); for (int i 0; i n; i) { if (i) cout ; if (i l) { cout 0.00; // padding 位固定 0不能把 beta 加进来 } else if (rms 0.0) { // 有效位全零且 eps 为 0归一化项按 0只留偏置 cout fmt(b[i]); } else { cout fmt(g[i] * h[i] / rms b[i]); } } cout | fmt(rms) | fmt(energy) \n; } return 0; }Javaimport java.util.*; public class Solution { // fmt 把数四舍五入到两位小数恰好满五时远离零|x|0.005 一律 0.00 static String fmt(double x) { if (Math.abs(x) 0.005) return 0.00; // 小到一定程度按 0.00 输出顺便去掉负零 // 先放大 100 倍按分取整正数向上取整、负数向下取整满五远离零 // String.format(%.2f) 走银行家舍入不能用 double cents x * 100; long c cents 0 ? (long) Math.floor(cents 0.5) : (long) Math.ceil(cents - 0.5); String s String.format(%d.%02d, Math.abs(c) / 100, Math.abs(c) % 100); return c 0 ? - s : s; } public static void main(String[] args) { Scanner sc new Scanner(System.in); int m sc.nextInt(); int n sc.nextInt(); double eps sc.nextDouble(); double[] g new double[n]; // 缩放向量 gamma按位置下标取 double[] b new double[n]; // 偏置向量 beta按位置下标取 for (int i 0; i n; i) g[i] sc.nextDouble(); for (int i 0; i n; i) b[i] sc.nextDouble(); StringBuilder sb new StringBuilder(); for (int k 0; k m; k) { int l sc.nextInt(); double alpha sc.nextDouble(); double[] x new double[n]; double[] r new double[n]; for (int i 0; i n; i) x[i] sc.nextDouble(); for (int i 0; i n; i) r[i] sc.nextDouble(); // 残差融合h x alpha * r double[] h new double[n]; for (int i 0; i n; i) h[i] x[i] alpha * r[i]; // energy 只累加前 l 个有效位置的平方padding 位不算 double energy 0.0; for (int i 0; i l; i) energy h[i] * h[i]; // l 为 0 时分母取 1防止除零 int d l 0 ? l : 1; double rms Math.sqrt(energy / d eps); for (int i 0; i n; i) { if (i 0) sb.append( ); if (i l) { sb.append(0.00); // padding 位固定 0不能把 beta 加进来 } else if (rms 0.0) { // 有效位全零且 eps 为 0归一化项按 0只留偏置 sb.append(fmt(b[i])); } else { sb.append(fmt(g[i] * h[i] / rms b[i])); } } sb.append( | ).append(fmt(rms)).append( | ).append(fmt(energy)).append(\n); } System.out.print(sb); } }Gopackage main import ( bufio fmt math os ) // format2dp 把数四舍五入到两位小数恰好满五时远离零|x|0.005 一律 0.00 func format2dp(x float64) string { if math.Abs(x) 0.005 { return 0.00 // 小到一定程度按 0.00 输出顺便去掉负零 } // math.Round 对 .5 一律远离零不走银行家舍入先放大 100 倍按分取整。 // 结果控制在 int64 范围内测试数据保证再拆成整数部分和小数部分 c : int64(math.Round(x * 100)) neg : c 0 if neg { c -c } s : fmt.Sprintf(%d.%02d, c/100, c%100) if neg { return - s } return s } func main() { // 输入量可能较大显式放大读入缓冲区 reader : bufio.NewReaderSize(os.Stdin, 1024*1024) var m, n int var eps float64 fmt.Fscan(reader, m, n, eps) g : make([]float64, n) // 缩放向量 gamma按位置下标取 b : make([]float64, n) // 偏置向量 beta按位置下标取 for i : 0; i n; i { fmt.Fscan(reader, g[i]) } for i : 0; i n; i { fmt.Fscan(reader, b[i]) } w : bufio.NewWriter(os.Stdout) defer w.Flush() for k : 0; k m; k { var l int var alpha float64 fmt.Fscan(reader, l, alpha) x : make([]float64, n) r : make([]float64, n) for i : 0; i n; i { fmt.Fscan(reader, x[i]) } for i : 0; i n; i { fmt.Fscan(reader, r[i]) } // 残差融合h x alpha * r h : make([]float64, n) for i : 0; i n; i { h[i] x[i] alpha*r[i] } // energy 只累加前 l 个有效位置的平方padding 位不算 energy : 0.0 for i : 0; i l; i { energy h[i] * h[i] } // l 为 0 时分母取 1防止除零 d : l if d 0 { d 1 } rms : math.Sqrt(energy/float64(d) eps) for i : 0; i n; i { if i 0 { w.WriteByte( ) } switch { case i l: w.WriteString(0.00) // padding 位固定 0不能把 beta 加进来 case rms 0.0: // 有效位全零且 eps 为 0归一化项按 0只留偏置 w.WriteString(format2dp(b[i])) default: w.WriteString(format2dp(g[i]*h[i]/rms b[i])) } } fmt.Fprintf(w, | %s | %s\n, format2dp(rms), format2dp(energy)) } }C#include stdio.h #include stdlib.h #include math.h /* 把数四舍五入到两位小数恰好满五时远离零|x|0.005 一律 0.00 */ static void fmt(double x, char *buf) { if (fabs(x) 0.005) { /* 不管正负小到一定程度都按 0.00 输出顺便去掉负零 */ sprintf(buf, 0.00); return; } /* llround 对 .5 一律远离零不走银行家舍入先放大 100 倍按分取整 */ long long cents llround(x * 100.0); if (cents 0) sprintf(buf, -%lld.%02lld, (-cents) / 100, (-cents) % 100); else sprintf(buf, %lld.%02lld, cents / 100, cents % 100); } int main(void) { int m, n; double eps; if (scanf(%d %d %lf, m, n, eps) ! 3) return 0; double *g malloc(sizeof(double) * n); /* 缩放向量 gamma按位置下标取 */ double *b malloc(sizeof(double) * n); /* 偏置向量 beta按位置下标取 */ double *x malloc(sizeof(double) * n); double *r malloc(sizeof(double) * n); double *h malloc(sizeof(double) * n); for (int i 0; i n; i) scanf(%lf, g[i]); for (int i 0; i n; i) scanf(%lf, b[i]); for (int k 0; k m; k) { int l; double alpha; scanf(%d %lf, l, alpha); for (int i 0; i n; i) scanf(%lf, x[i]); for (int i 0; i n; i) scanf(%lf, r[i]); /* 残差融合h x alpha * r */ for (int i 0; i n; i) h[i] x[i] alpha * r[i]; /* energy 只累加前 l 个有效位置的平方padding 位不算 */ double energy 0.0; for (int i 0; i l; i) energy h[i] * h[i]; /* l 为 0 时分母取 1防止除零 */ int d l 0 ? l : 1; double rms sqrt(energy / d eps); for (int i 0; i n; i) { char buf[64]; if (i) putchar( ); if (i l) { fputs(0.00, stdout); /* padding 位固定 0不能把 beta 加进来 */ } else if (rms 0.0) { /* 有效位全零且 eps 为 0归一化项按 0只留偏置 */ fmt(b[i], buf); fputs(buf, stdout); } else { fmt(g[i] * h[i] / rms b[i], buf); fputs(buf, stdout); } } char buf1[64], buf2[64]; fmt(rms, buf1); fmt(energy, buf2); printf( | %s | %s\n, buf1, buf2); } free(g); free(b); free(x); free(r); free(h); return 0; }Pythonimport sys import math def fmt(x): # 绝对值不足 0.005 的一律印成 0.00顺带避免 -0.00 if abs(x) 0.005: return 0.00 # 放大 100 倍按分取整正数向上取整、负数向下取整 # 恰好满五时远离零正是题目要的标准四舍五入 cents x * 100 c int(math.floor(cents 0.5)) if cents 0 else int(math.ceil(cents - 0.5)) s f{abs(c) // 100}.{abs(c) % 100:02d} return - s if c 0 else s def main(): data sys.stdin.read().split() pos 0 def nxt(): nonlocal pos v data[pos] pos 1 return v m int(nxt()) n int(nxt()) eps float(nxt()) g [float(nxt()) for _ in range(n)] # 缩放向量 gamma按位置下标取 b [float(nxt()) for _ in range(n)] # 偏置向量 beta按位置下标取 out [] for _ in range(m): l int(nxt()) alpha float(nxt()) x [float(nxt()) for _ in range(n)] r [float(nxt()) for _ in range(n)] # 残差融合h x alpha * r h [x[i] alpha * r[i] for i in range(n)] # energy 只累加前 l 个有效位置的平方padding 位不算 energy 0.0 for i in range(l): energy h[i] * h[i] # l 为 0 时没有有效位分母取 1 防止除零 d l if l 0 else 1 rms (energy / d eps) ** 0.5 vals [] for i in range(n): if i l: # padding 位固定输出 0不能把 beta 加进来 vals.append(0.00) elif rms 0.0: # 有效位全是 0 且 eps 为 0RMS 为 0归一化项按 0只留偏置 vals.append(fmt(b[i])) else: vals.append(fmt(g[i] * h[i] / rms b[i])) out.append( .join(vals) | fmt(rms) | fmt(energy)) sys.stdout.write(\n.join(out) \n) main()