본문 바로가기

python

[코딩] 넘파이 모듈을 사용하지 않고 행렬곱 코드 짜기(python)

728x90

 전공 수업인 데이터 마이닝 오티날에 깜짝 코딩 테스트를 치뤘다. 50분 동안 2문제를 푸는 거였는데 생각보다 어려웠어서 코드를 정리하고 간다. 

 


행렬곱이 뭘까?

 두 행렬의 곱을 의미한다. 

 

출처 나무위키

 

 행렬곱은 일반적인 원소곱과 다르다. 뭔지 잘 모른다면 알아보고 오길 바란다. 

 


넘파이 모듈을 사용한 행렬곱

import numpy as np
a=np.array([[2,3,0],[8,9,1],[7,0,5]])
b=np.array([[1,2,2],[1,2,1],[1,2,3]])
print(np.dot(a,b))

 


모듈을 사용하지 않은 행렬곱

  클래스로 짜서 꽤 길어보이지만 핵심 코드는 run 메소드다. 

class matrix_mul:
    def __init__(self, a, b):
        self.a=a
        self.b=b
        self.result=[[0 for j in range(len(b[0]))] for i in range(len(a))]
        
    def run(self):
        r=0
        for i in range(len(self.a)):
            for j in range(len(self.b[0])):
                for k in range(len(self.b)): 
                    r+=self.a[i][k]*self.b[k][j]
                self.result[i][j]=r; r=0

    def __str__(self):
        return "{}".format(self.result)

if __name__=="__main__":
    a=[[2,3,0],[8,9,1],[7,0,5]]
    b=[[1,2,2],[1,2,1],[1,2,3]]
    
    answer=matrix_mul(a,b)
    answer.run()
    print(answer)

 

 일단 예제에 나온 행렬은 모양이 똑같아서 다른 행렬로 하겠다. 

    c=[[1,3,2],[4,0,1]]
    d=[[1],[0],[5]]
    def run(self):
        r=0
        for i in range(len(self.a)):
            for j in range(len(self.b[0])):
                for k in range(len(self.b)): 
                    r+=self.a[i][k]*self.b[k][j]
                self.result[i][j]=r; r=0

 


for문 구조?

 행렬곱을 구하는 과정을 풀어서 보면 규칙이 있는 것이 보일 거다. 이 부분이 문제를 푸는 포인트다. 아마 많이 헷갈릴테니 직접 그려보는 것을 추천한다. 

 

 그림에서 다음의 도형을 친 것들은 그 모양이 똑같은 것들이다.

 

  • 샤프로 동그라미 
  • 샤프로 세모 
  • 빨간색 동그라미
  • 빨간색 세모

 

 여기서 규칙을 찾을 수 있다. 빨간색 형광펜을 친 부분이 가장 바깥쪽 for문에 들어가고, 초록색으로 친 부분은 그 다음, 파란색 동그라미가 마지막 for문으로 들어간다. 아직 이해가 되지 않을 테니 코드를 들어가보자. 

 

 


코드 리뷰

1. result[] 초기화

 행렬이 2행 3열 * 3행 2열이면, 앞쪽 행렬의 열과 뒤쪽 행렬의 행의 값이 같아야 하고, 이때 두 행렬의 곱의 결과는 앞쪽 행렬의 행 * 뒤쪽 행렬의 열이라는 것을 알 것 이다.

 self.result=[[0 for j in range(len(b[0]))] for i in range(len(a))]

 참고로 빈 리스트를 만들 때 *연산자보다 for문을 쓰는 것을 추천한다. 이유는 아래 링크 참고. 

https://sogogi1000inbun.tistory.com/33

결과를 확인해보면 정상적으로 2행 2열로 들어갔다

 


 

2. for문

 우리는 결과값인 result[0][0] 값부터 먼저 구할 거다. result[0][0] 값은 아래 그림의 값들을 모두 더했을 때 구할 수 있다. 

 

 

  • 가장 바깥쪽 for문은 a행렬의 행을 지정해준다. 
  • 2번째 for문은 a행렬의 열과 b행렬의 행을 지정해준다. 
  • 마지막 for문은 a행렬의 열을 지정해준다.

 

 행렬곱 순서를 생각해보면 for문의 range값에 들어가는 값이 왜 저렇게 들어가는 지 이해하기 쉬울 것이다. 

    def run(self):
        r=0
        for i in range(len(self.a)):
            for j in range(len(self.b[0])):
                for k in range(len(self.b)): 
                    r+=self.a[i][k]*self.b[k][j]
                self.result[i][j]=r; r=0

 


 아마 여기까지 읽어도 감이 잘 안 올텐데, 직접 써보고 코드를 돌려봐야 감이 올 거다. 그럼 아디오스