앞서 행렬이 뭔지 대충 알아봤다.
프로그래머스 행렬의 곱셈을 풀기위해 행렬의 곱셈을 암산으로 구현하려다 좀 정리할 필요가 있어 공부했다.
결론은 다시 코드로 구현해야 하므로 for문을 어떤식으로 잘 짜야할지 생각해보자
기초
전기기초 수학 - 9.쉽게 이해하는 행렬 계산, 행렬의 곱셈 및 전치행렬
안녕하세요? 소망 김기사입니다. 이번 포스팅은 행렬에 대해 이야기 하고자 합니다. 행렬(行列, metrix)은 ...
blog.naver.com
다음은 A와B의곱인 AB를 만드는 법이다.

코드를 하기 위해서는 이 과정의 정리와 이해가 필요하고 for문으로 표현할수 있어야 한다.
코드의 이해를 쉽게 하기 위해 A의 dim은 3x2, B의 dim은 2x4으로 했다.
# 일단 2개 행렬을 리스트로 표현하면
a = [
[a11, a12],
[a21, a22],
[a31, a32],
]
b = [
[b11, b12, b13,b14],
[b21, b22, b23,b24]
]
A x B의 곱 AB의 형상은 a의 row x b의 column 이 된다. 즉 3 x 4
ab = [
[ab11, ab12, ab13, ab14],
[ab21, ab22, ab23, ab24],
[ab31, ab32, ab33, ab34]
]
# a의 형상dim은 ar x ac, b는 br x bc 이라고 하자 앞서 설명을 보면 mxn ixj라고 하던 부분이다. 프로그래머니까 row와 column의 약자를 섰다.
a = [[ i*10+j for j in range(1,3)] for i in range(1,4)]
b = [[ i*10+j for j in range(1,5)] for i in range(1,3)]
ar, bc = len(a), len(b[0])
ab = [[ i*10+j for j in range(1,bc+1)] for i in range(1,ar + 1)]
print('a', *a, 'b', *b,'ab',*ab, sep='\n')
출력, 형상확인
a
[11, 12]
[21, 22]
[31, 32]
b
[11, 12, 13, 14]
[21, 22, 23, 24]
ab
[11, 12, 13, 14]
[21, 22, 23, 24]
[31, 32, 33, 34]
결론 ab의 형상은 ar, bc만 알면 된다.
하지만 실제 A와B의 행렬곱 AB의 계산은 의사적으로 ABij = sum([Aik*Bkj])이다 정확한건 앞의 이론 참조
하지만 우린 형상을 만들었기 때문에 ABij를 계산해주는 dot()함수만 구현하면 된다.
a의 행과 b의 열의 집합을 곱해줘야 하는데 행이야 a[i] 로 쉽지만 b리스트의 j번째 열의 집합은 모든 리스트를 순회해야 한다.
j=2
bj = [v for line in b for i,v in enumerate(line) if i==j]
print(bj)
그런데 파이썬에 이걸 간단하게 하는 법이 있다. *을 붙여 리스트를 풀어주고 zip으로 column별로 정리해준걸 다시 list로 변환하면 된다. zip은 보통 여러개의 리스트를 열별로 정리해주는데 사용했는데 이런식으로 사용하면 전치행렬로 응용할수 있다.
list(zip(*b))
이렇게 하면 이유는 AB의 행렬곱은 A와B를ㄱ으로 곱해주기 때문이다. 이건 아시리라 믿는다 B를 전치행렬로 만들경우 A와B의 컬럼수가 같아지고 컬럼끼리 곱하니까 구현이 쉬워진다. 결국 행렬곱은 두 행렬의 형상이 맞어야 하고 코드 구현을 쉽게 하기 위해 뒷부분을 전치하는거지 실제는 아니다
a =
[
[1, 2]
[1, 2]
[1, 2]
]
b =
[
[1, 1, 1, 1]
[2, 2, 2, 2]
]
b.T =
[
[1,2]
[1,2]
[1,2]
[1,2]
]
계산체크를 쉽게 하기 위해 a는 j값 b는 i로 정의
def dot(a, b):
return sum(i * j for i, j in zip(a, b))
a = [[j for j in range(1, 3)] for i in range(1, 4)]
b = [[i for j in range(1, 5)] for i in range(1, 3)]
print('a', *a, 'b', *b, sep='\n')
bt = list(zip(*b))
ab = [[dot(i, j) for j in bt] for i in a]
print('ab', *ab, sep='\n')
ps 하지만 gpt는 행렬계산시 전치는 자주 쓰인다고 한다.
네, 실제 행렬곱 계산에서는 전치행렬을 사용하는 경우가 많습니다. 특히, 컴퓨터 과학, 수치해석, 머신러닝 분야에서는 전치행렬을 이용해 계산을 더 효율적이고 간단하게 하는 전략이 흔히 활용됩니다. 아래는 그 이유와 사례를 설명합니다:
1. 병렬화와 최적화 용이:
- 전치행렬을 사용하면 데이터 접근 패턴이 연속적이게 되어 메모리 캐시 활용도가 높아지고, 벡터화와 병렬처리(예: SIMD 명령어, GPU 가속)가 더 용이합니다.
2. 행렬 곱셈의 수식을 간소화:
- 원래의 행렬 곱셈은 \( C = AB \) 로 표기하는데, 여기서 \( C_{i,j} = \sum_k A_{i,k} B_{k,j} \).
- 만약 \( B \)의 전치 \( B^T \)를 사용한다면, \( C_{i,j} = \sum_k A_{i,k} B^T_{j,k} \).
- 즉, \( B \)가 아니라 \( B^T \)를 이용하면, \( C_{i,j} \)는 \( A \)의 한 행과 \( B^T \)의 한 행(이전의 \( B \)의 열)이 곱셈됩니다.
- 이렇게 하면, 내부 루프를 행단위(연속된 메모리 위치)로 돌리기가 쉽고, 최적화가 용이합니다.
3. 라이브러리와 프레임워크의 실무 활용:
- 행렬곱 라이브러리(NumPy, BLAS, cuBLAS 등)는 대부분 이 원리를 활용하여 내부적으로 전치와 배치 최적화를 수행합니다.
- 예를 들어, NumPy의 `np.dot()` 또는 `@` 연산자는 내부적으로 전치와 블록 연산을 이용해 최적화된 코드가 작동합니다.
4. 실습 예시:
- 만약 \( A \)와 \( B \)가 크거나, 반복 계산이 많을 때는 \( B \)를 전치시켜서 계속 사용할 수 있습니다.
- 또는, \( A \)와 \( B^T \)를 미리 만들어 놓고 계산하면 루프를 간단하게 해서 속도를 높일 수 있습니다.
요약하면, 전치행렬을 활용한 행렬 곱셈은 수학적 계산을 간단하게 할 뿐만 아니라, 프로그래밍적 최적화, 특히 대규모 연산에서 매우 유용한 전략입니다.
'알고리즘' 카테고리의 다른 글
| 프로그래머스 모음사전 (1) | 2025.09.01 |
|---|---|
| 행렬 클래스, 선형대수, 매트릭스2 (2) | 2025.08.31 |
| 파이썬 순열 재귀 Permutation (2) | 2025.07.31 |