프로그래머스 12949 - 행렬의 곱셈 (Lv.2)
프로그래머스 12949 - 행렬의 곱셈 (Lv.2)
출처: https://school.programmers.co.kr/learn/courses/30/lessons/12949
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
// 프로그래머스 12949 - 행렬의 곱셈 (Lv.2)
// https://school.programmers.co.kr/learn/courses/30/lessons/12949
// 문제 설명
// 2차원 행렬 arr1, arr2가 주어질 때 arr1 x arr2의 행렬 곱을 반환하라.
// 곱할 수 있는 짝(arr1 열 수 == arr2 행 수)만 입력으로 들어온다.
// 제약 조건
// 행렬의 행/열 길이는 2 이상 100 이하
// 원소는 -10 이상 20 이하
// Example
// Input : arr1 = [[1,4],[3,2],[4,1]], arr2 = [[3,3],[3,3]]
// Output: [[15,15],[15,15],[15,15]]
// 접근 — 정의대로 3중 루프, 순회 순서만 i-k-j
// 1) 결과는 r x c (r = arr1 행 수, c = arr2 열 수), 공유 차원 m = arr2 행 수.
// answer[i][j] = sum(arr1[i][k] * arr2[k][j]) 를 그대로 계산한다.
// 2) 루프를 i-j-k 대신 i-k-j로 돌리면 안쪽 루프가 arr2[k][0..c-1]를
// 행 방향(연속 메모리)으로 읽어 캐시 적중률이 좋아진다.
// 3) 결과를 0으로 초기화해 두고 누적하면 별도 임시 변수가 필요 없다.
// 시간 O(r*m*c) — 최악 100^3 = 100만, 공간 O(r*c)
#include <vector>
using namespace std;
vector<vector<int>> solution(vector<vector<int>> arr1, vector<vector<int>> arr2) {
int r = arr1.size(), c = arr2[0].size(), m = arr2.size();
vector<vector<int>> answer(r, vector<int>(c, 0));
for (int i = 0; i < r; i++)
for (int k = 0; k < m; k++) // i-k-j 순서: arr2를 행 방향으로 읽어 캐시 친화적
for (int j = 0; j < c; j++)
answer[i][j] += arr1[i][k] * arr2[k][j];
return answer;
}
정리
- 결과 행렬의 크기부터 고정하고 시작한다.
(r x m) x (m x c) = (r x c)— 행은 arr1에서, 열은 arr2에서 온다. 이 크기 계산이 흔들리면 인덱스 범위가 전부 꼬이므로 코드 첫 줄에서r, c, m을 이름 붙여 박아두는 게 안전하다. - 교과서식
i-j-k순서는 안쪽 루프에서arr2[k][j]의 k가 변해 열 방향(불연속 메모리)으로 점프한다.i-k-j로 바꾸면 안쪽 루프가arr2[k][0..c-1]을 연속으로 읽어 캐시 라인을 그대로 태운다. 이 제약(100^3)에서는 체감 차이가 없지만, 행렬이 커지면 수 배 차이가 나는 표준 최적화다. i-k-j가 성립하려면 결과를 미리 0으로 초기화해 두고 누적해야 한다(vector<int>(c, 0)). j 루프가 완결되기 전에 다른 k가 같은 칸에 더해지기 때문이다.- 원소 최대 20, 공유 차원 최대 100이면 한 칸의 최댓값은 20 x 20 x 100 = 40,000이라 int로 충분하다. 곱셈 문제를 만나면 누적값의 상한부터 계산해 자료형을 정하는 습관이 오버플로 사고를 막는다.
- 검증: 예제
[[1,4],[3,2],[4,1]] x [[3,3],[3,3]] → [[15,15],[15,15],[15,15]],[[2,3,2],[4,2,4],[3,1,4]] x [[5,4,3],[2,4,1],[3,1,1]] → [[22,22,11],[36,28,18],[29,20,14]]통과(MSVC/std:c++17컴파일·실행).
이 기사는 저작권자의 CC BY 4.0 라이센스를 따릅니다.