CCF-CSP 202305-2 矩阵计算
😸题目要求
🐈⬛题目背景
S o f t m a x ( Q × K T d ) × V Softmax(\frac{Q×{K^T}}{\sqrt{d}})×V Softmax(dQ×KT)×V 是 Transformer 中注意力模块的核心算式,其中 Q Q Q、 K K K 和 V V V 均是 n n n 行 d d d 列的矩阵, K T K^T KT 表示矩阵 K K K 的转置, × × × 表示矩阵乘法。
🐈⬛问题描述
为了方便计算,顿顿同学将
S
o
f
t
m
a
x
Softmax
Softmax 简化为了点乘一个大小为
n
n
n 的一维向量
W
W
W:
(
W
⋅
(
Q
×
K
T
)
)
×
V
\quad \quad \quad \quad \quad \quad \quad \quad \quad \quad \quad \quad \quad \quad(W·(Q×{K^T}))×V
(W⋅(Q×KT))×V
点乘即对应位相乘,记
W
(
i
)
W^{(i)}
W(i) 为向量
W
W
W 的第
i
i
i 个元素,即将
(
Q
×
K
T
)
(Q×K^T)
(Q×KT) 第
i
i
i 行中的每个元素都与
W
(
i
)
W^{(i)}
W(i) 相乘。
现给出矩阵
Q
Q
Q、
K
K
K 和
V
V
V 和向量
W
W
W,试计算顿顿按简化的算式计算的结果。
🐈⬛输入格式
从标准输入读入数据。
输入的第一行包含空格分隔的两个正整数
n
n
n 和
d
d
d,表示矩阵的大小。
接下来依次输入矩阵
Q
Q
Q、
K
K
K 和
V
V
V 。每个矩阵输入
n
n
n 行,每行包含空格分隔的
d
d
d 个整数,其中第
i
i
i 行的第
j
j
j 个数对应矩阵的第
i
i
i 行、第
j
j
j 列。
最后一行输入
n
n
n 个整数,表示向量
W
W
W。
🐈⬛输出格式
输出到标准输出中。
输出共
n
n
n 行,每行包含空格分隔的
d
d
d 个整数,表示计算的结果。
🐈⬛样例输入
3 2
1 2
3 4
5 6
10 10
-20 -20
30 30
6 5
4 3
2 1
4 0 -5
🐈⬛样例输出
480 240
0 0
-2200 -1100
🐈⬛子任务
70% 的测试数据满足:
n
≤
100
n \leq 100
n≤100 且
d
≤
10
d \leq 10
d≤10;输入矩阵、向量中的元素均为整数,且绝对值均不超过
30
30
30。
全部的测试数据满足:
n
≤
1
0
4
n \leq 10^4
n≤104 且
d
≤
20
d \leq 20
d≤20;输入矩阵、向量中的元素均为整数,且绝对值均不超过
1000
1000
1000。
提示
请谨慎评估矩阵乘法运算后的数值范围,并使用适当数据类型存储矩阵中的整数。
😸问题解决
🐈满分代码(含逐行代码解释)
🍭C++
#include<bits/stdc++.h>
using namespace std;
long long Q[10001][21];
long long K[10001][21];
long long V[10001][21];
long long W[10001];
long long temp1[21][21]; //d*d的矩阵
long long temp2[10001][10001]; //n*n的矩阵
long long result[10001][21]; //n*d的矩阵
int main(){
//输入
int n, d;
cin >> n >> d;
for(int i = 0; i < n; i++){
for(int j = 0; j < d; j++){
cin >> Q[i][j];
}
}
for(int i = 0; i < n; i++){
for(int j = 0; j < d; j++){
cin >> K[i][j];
}
}
for(int i = 0; i < n; i++){
for(int j = 0; j < d; j++){
cin >> V[i][j];
}
}
for(int i = 0; i < n; i++){
cin >> W[i];
}
//计算
/*
首先解释一下为什么没有按照正常计算顺序进行运算:
·矩阵乘法可以使用结合律而不能使用交换律
·先算n*n大小的矩阵一是代码不好表示,二是时间和空间复杂度较高,所以不采用
·先算d*d大小的矩阵可以在获得正确答案的基础上简化计算过程,这个数学原理显而易见
·这个就是三行二列乘二行三列和二行三列乘三行二列的区别
·计算过程中尤其要主要三层循环中每一层的变量取值范围,看清楚是几行几列的矩阵,建议在纸上写一些
·矩阵乘法牢记下标 c_ij = a_ik * b_kj
*/
/*先算K^T* x V*/
for(int i = 0; i < d; i++){
for(int j = 0; j < d; j++){
temp1[i][j] = 0; //初始一个空矩阵存放此步结果
for(int k = 0; k < n; k++){
temp1[i][j] += K[k][i] * V[k][j]; //矩阵K的转置乘矩阵V
//结果是d*d的矩阵
}
}
}
/*再算(Q x K^T) x V*/
for(int i = 0; i < n; i++){
for(int j = 0; j < d; j++){
temp2[i][j] = 0; //初始一个空矩阵存放此步结果
for(int k = 0; k < d; k++){
temp2[i][j] += Q[i][k] * temp1[k][j]; //矩阵Q乘上一步的计算结果
//结果是n*d的矩阵
}
}
}
/*最后算(W * (Q x K^T)) x V)*/
for(int i = 0; i < n; i++){
for(int j = 0; j < d; j++){
result[i][j] = 0; //初始一个空矩阵存放最终结果
result[i][j] = W[i] * temp2[i][j]; //乘以一维向量W[i]
//结果是n*d的矩阵
}
}
//输出
for(int i = 0; i < n; i++){
for(int j = 0; j < d; j++){
cout << result[i][j] << " "; //输出一个n*d的矩阵
}
cout << endl; //注意矩阵输出是n*d行
}
return 0;
}
🐈场景拓展
本题代码用于求解矩阵相关的算法,该代码还可能涉及如下考点:
- 矩阵乘法、矩阵转置,以及其余矩阵相关的数学计算
- 针对矩阵计算过程中关于结合律等运算性质的考察
- 关于时间复杂度和空间复杂度的优化过程