본문 바로가기

행렬의 모양·덧셈·곱셈

행·열 크기 조건을 검사하고 행과 열의 내적으로 곱을 만듭니다.

그래프의 행렬을 읽었다면 숫자를 담은 행렬 연산도 할 수 있습니다. 목표는 행·열 크기를 먼저 정하고 행렬 곱을 원소별 곱과 구분하는 것입니다. 중첩 리스트·반복·곱셈을 복습하세요.

개념과 약속

행렬은 열 수가 같은 행들로 이루어진 숫자 표입니다. m행 k열은 m×k입니다. 덧셈은 모양이 같은 두 행렬의 같은 칸을 더합니다. 곱셈 A(m×k)·B(k×n)은 A의 열 수와 B의 행 수가 같아야 하며 결과는 m×n입니다. 결과 칸 (i,j)는 A의 행 i와 B의 열 j를 대응해 곱한 뒤 모두 더한 값입니다.

아래 입력은 직사각형 중첩 리스트입니다. []는 0×0으로 허용하지만 [[]]처럼 원소가 없는 행과 행 길이가 다른 입력은 거부합니다. 결과 각 행을 독립적으로 만드는 리스트 컴프리헨션을 씁니다. [[0]*n]*m은 같은 행을 여러 번 참조할 수 있어 이후 한 칸 변경이 다른 행에도 나타나는 문제에 주의하세요.

행·열의 대응 곱을 더해 한 칸을 만든다

결과의 첫 셀에 필요한 두 벡터를 읽는다. 부분 곱을 합해 결과 셀을 채운다. 각 단계의 값과 전환 이유를 비교합니다.

자료구조·알고리즘 · 상태와 관계

행·열의 대응 곱을 더해 한 칸을 만든다

A 첫 행 1,2와 B 첫 열 5,7을 읽어 1×5+2×7=19를 계산합니다.

행·열의 대응 곱을 더해 한 칸을 만든다A행의값1,2와B열을가로로펼쳐놓은값5,7이대응된다.두곱1곱하기5와2곱하기7을더해결과한칸19를만든다.A의 행과 B의 열을 대응1257A행B열1×52×719한 결과 칸 = 5 + 14

위 두 줄은 한 행과 한 열의 값들을 대응 위치에 펼친 것입니다. 전체 행렬의 모양을 그린 것이 아닙니다. 화살표는 곱과 합의 계산 의존 관계입니다.

이 예의 조건

  • 본문과 전체 도표의 예를 따라 행과 열 선택부터 합해서 기록까지 진행합니다.
관계도의 보충 설명
전체 도표의 표기·관계·조건
구분전체 도표의 내용
관계 설명A 행의 값 1,2와 B 열을 가로로 펼쳐 놓은 값 5,7이 대응된다. 두 곱 1 곱하기 5와 2 곱하기 7을 더해 결과 한 칸의 값 19를 만든다.
노드·선 표기A 행
노드·선 표기B 열
예의 값과 변화
관찰·상황값·보충 정보변화·이유
행과 열 선택

결과의 첫 셀에 필요한 두 벡터를 읽는다.

  • 행·열·결과 1: 행 [1,2]
  • 행·열·결과 2: 열 [5,7]
  • 행·열·결과 3: 결과 셀 (0,0)
  • (0,0)의 대응 항을 선택
대응 항 곱하기

같은 위치의 두 항을 각각 곱한다.

  • 행·열·결과 1: 1×5=5
  • 행·열·결과 2: 2×7=14
  • 두 부분 곱 5와 14 준비
합해서 기록

부분 곱을 합해 결과 셀을 채운다.

  • 행·열·결과 1: 5+14=19
  • 행·열·결과 2: 결과 (0,0)=19
  • 결과 셀이 미계산에서 19로 변경

행·열의 대응 곱을 더해 한 칸을 만든다

결과의 첫 셀에 필요한 두 벡터를 읽는다. 부분 곱을 합해 결과 셀을 채운다. 각 단계의 값과 전환 이유를 비교합니다.

자료구조·알고리즘 · 상태와 관계

행·열의 대응 곱을 더해 한 칸을 만든다

A 첫 행 1,2와 B 첫 열 5,7을 읽어 1×5+2×7=19를 계산합니다.

행·열의 대응 곱을 더해 한 칸을 만든다A행의값1,2와B열을가로로펼쳐놓은값5,7이대응된다.두곱1곱하기5와2곱하기7을더해결과한칸19를만든다.A의 행과 B의 열을 대응1257A행B열1×52×719한 결과 칸 = 5 + 14

위 두 줄은 한 행과 한 열의 값들을 대응 위치에 펼친 것입니다. 전체 행렬의 모양을 그린 것이 아닙니다. 화살표는 곱과 합의 계산 의존 관계입니다.

이 예의 조건

  • 본문과 전체 도표의 예를 따라 행과 열 선택부터 합해서 기록까지 진행합니다.

관계도의 보충 설명

전체 도표의 표기·관계·조건
구분전체 도표의 내용
관계 설명A 행의 값 1,2와 B 열을 가로로 펼쳐 놓은 값 5,7이 대응된다. 두 곱 1 곱하기 5와 2 곱하기 7을 더해 결과 한 칸의 값 19를 만든다.
노드·선 표기A 행
노드·선 표기B 열
예의 값과 변화
관찰·상황값·보충 정보변화·이유
행과 열 선택

결과의 첫 셀에 필요한 두 벡터를 읽는다.

  • 행·열·결과 1: 행 [1,2]
  • 행·열·결과 2: 열 [5,7]
  • 행·열·결과 3: 결과 셀 (0,0)
  • (0,0)의 대응 항을 선택
대응 항 곱하기

같은 위치의 두 항을 각각 곱한다.

  • 행·열·결과 1: 1×5=5
  • 행·열·결과 2: 2×7=14
  • 두 부분 곱 5와 14 준비
합해서 기록

부분 곱을 합해 결과 셀을 채운다.

  • 행·열·결과 1: 5+14=19
  • 행·열·결과 2: 결과 (0,0)=19
  • 결과 셀이 미계산에서 19로 변경

손으로 상태 추적하기

A=[[1,2],[3,4]], B=[[5,6],[7,8]]입니다.

결과 칸행·열 계산값
(0,0)1×5 + 2×719
(0,1)1×6 + 2×822
(1,0)3×5 + 4×743
(1,1)3×6 + 4×850

직접 실행하기

아래 코드는 이 본문만으로 실행할 수 있습니다. lesson.py로 저장하고 첫 장에서 확인한 Python 명령으로 실행하세요.

중첩 리스트 컴프리헨션에서 안쪽은 결과 한 행의 각 열을 만들고 바깥쪽은 모든 행을 만듭니다. sum(...)은 k별 곱들을 모두 더합니다. zip은 대응하는 두 행 원소를 한 쌍씩 읽으며, 이 코드에서는 모양 검사를 먼저 해 길이 누락을 막습니다.

def matrix_shape(matrix):
    if not matrix:
        return 0, 0
    columns = len(matrix[0])
    if columns == 0 or any(len(row) != columns for row in matrix):
        raise ValueError("nonempty rectangular rows required")
    return len(matrix), columns


def matrix_add(first, second):
    if matrix_shape(first) != matrix_shape(second):
        raise ValueError("equal shapes required")
    return [[a + b for a, b in zip(row_a, row_b)] for row_a, row_b in zip(first, second)]


def matrix_multiply(first, second):
    rows, shared = matrix_shape(first)
    other_rows, columns = matrix_shape(second)
    if shared != other_rows:
        raise ValueError("first columns must equal second rows")
    return [[sum(first[i][k] * second[k][j] for k in range(shared))
             for j in range(columns)] for i in range(rows)]


a = [[1, 2], [3, 4]]
b = [[5, 6], [7, 8]]
print(matrix_add(a, b))
print(matrix_multiply(a, b))
print(matrix_multiply(b, a))
try:
    matrix_add([[1]], [[1, 2]])
except ValueError:
    print("모양 불일치 거부")

실행 결과:

[[6, 8], [10, 12]]
[[19, 22], [43, 50]]
[[23, 34], [31, 46]]
모양 불일치 거부

왜 맞고 언제 끝나는가

공통 인덱스 k를 같은 범위로 순회하면 각 행·열에 필요한 모든 대응 곱을 한 번씩 더합니다. 모양 검사가 범위 접근을 보장합니다. 결과 행 i·열 j를 모두 생성하므로 빠진 칸이 없습니다. 세 범위가 유한해서 종료합니다.

비용과 적용 범위

덧셈은 O(mn), 일반적인 세 반복 곱셈은 O(mkn) 시간이며 결과 공간 O(mn)입니다. 이 교육용 구현은 수치 안정성·벡터화 최적화·희소 행렬을 다루는 전문 라이브러리의 대체가 아닙니다. 작은 정수로 정확한 상태를 확인하는 목적입니다.

스스로 풀어 보기

  1. 2×3 행렬과 3×4 행렬의 곱은 가능한가요? 결과 모양은 무엇인가요?
  2. AB와 BA가 항상 같다는 주장을 위 출력으로 판단하세요.

힌트

  1. 안쪽 두 수가 같아야 합니다.
  2. (0,0)부터 값이 다릅니다.

해설

  1. 가능하며 2×4입니다.
  2. 일반적으로 같지 않습니다. 이 예에서 19와 23 등 값이 다릅니다. 또한 AB가 가능해도 BA의 크기 조건이 맞지 않을 수 있습니다.

흔한 오해

행렬 곱은 같은 위치끼리만 곱하는 연산이 아닙니다. 그래프 인접 행렬의 숫자 곱과 Warshall의 불리언 식도 목적이 다릅니다.

복습과 다음 연결

선택 심화 Shell 정렬로 넘어가거나 종합 길 찾기로 먼저 복습할 수 있습니다.