본문 바로가기

Computer/Coding Test

파이썬으로 행렬 구현하기 (2)

반응형

[알고리즘 & 코딩테스트/코딩테스트] - 파이썬으로 행렬 구현하기 (1)


이번 포스트에서는 지난 포스트에서 구현한 행렬 클래스를 기반으로 덧셈, 뺄셈, 곱셈 등의 다양한 행렬 연산을 구현해보도록 하겠습니다. 이때 행렬 클래스끼리 더하거나 곱하는 등의 연산이 가능하도록 언더바 두개로 이루어진 magic method (__magic_method__) 함수를 정의하는 방식으로 구현하도록 하겠습니다.

 

덧셈과 뺄셈

먼저 덧셈을 구현합니다. 주의 사항으로는 더할 두 행렬의 행과 열의 크기가 같아야하고 만약 정수를 더할 때에는 기존 행렬의 행 혹은 열의 크기가 1일 경우 (벡터의 경우) 모든 원소에 정수를 더하고 행, 열의 크기가 모두 1보다 크다면 대각선 원소만 더한다고 가정합니다. 만약 기존 행렬의 행, 열의 크기가 1보다 크지만 같지 않다면 (정방행렬이 아닌 경우) 계산을 하지 않도록 에러를 도출합니다.

    def __add__(self, A):
        '''
        Add matrix by elements, both matrices must have the same size
        Examples:
        >>> A = Matrix([[4,3,0],[2,1,0]])
        >>> B = Matrix([[1,2,0],[3,4,0]])
        >>> C = A+B
        '''

        if not isinstance(A, Matrix):  # A가 행렬이 아닐 경우 (int)
            if self.nrows == self.ncols:  
                A = Matrix.identity(self.nrows, A)
            elif self.nrows == 1 or self.ncols == 1:
                A = Matrix([[A for c in range(self.ncols)] for r in range(self.nrows)])
            else:
                raise RuntimeError('inavailable add')

        if A.nrows != self.nrows or A.ncols != self.ncols:  # Does not match shape
            raise ArithmeticError('incompatible dimensions')

        C = Matrix(self.nrows, self.ncols)
        for r in range(self.nrows):
            for c in range(self.ncols):
                C[r,c] = A[r,c] + self[r,c]
        return C
        
 
A = Matrix([[4,3,0], [2,1,0]])
B = Matrix([[1,2,0], [3,4,0]])
C = A+B
print(C)
>>> [[5, 5, 0], [5, 5, 0]]

A = Matrix([[4,3], [2,1]])
B = 1
C = A+B
print(C)
>>> [[5, 3.0], [2.0, 2]]

a = Matrix([[1],[2],[3]])
print(a + 2)
>>> [[3], [4], [5]]

 

비슷하게 뺄셈을 구현합니다. 또한, 대상 객체가 +나 - 연산자의 오른쪽에 있을 때에도 동작하도록 __radd__, __rsub__ 메소드를 구현하고 음수로 전환할 수 있도록 __neg__ 메소드도 추가적으로 구현해 주겠습니다.

def __sub__(self, A):
        '''
        Add matrix by elements, both matrices must have the same size
        Examples:
        >>> A = Matrix([[4,3,0],[2,1,0]])
        >>> B = Matrix([[1,2,0],[3,4,0]])
        >>> C = A-B
        '''

        if not isinstance(A, Matrix):  # A가 행렬이 아닐 경우 (int)
            if self.nrows == self.ncols:  
                A = Matrix.identity(self.nrows, A)
            elif self.nrows == 1 or self.ncols == 1:
                A = Matrix([[A for c in range(self.ncols)] for r in range(self.nrows)])
            else:
                raise RuntimeError('inavailable add')

        if A.nrows != self.nrows or A.ncols != self.ncols:  # Does not match shape
            raise ArithmeticError('incompatible dimensions')

        C = Matrix(self.nrows, self.ncols)
        for r in range(self.nrows):
            for c in range(self.ncols):
                C[r,c] = self[r,c] - A[r,c]
        return C

    def __radd__(self, A):
        return self + A
    
    def __rsub__(self, A):
        return self - A
        
    def __neg__(self):
        return Matrix(self.nrows, self.ncols, fill=lambda r, c: -self[r, c])
        
      
A = Matrix([[4,3,0],[2,1,0]])
B = Matrix([[1,2,0],[3,4,0]])
C = A-B
print(C)
>>> [[3, 1, 0], [-1, -3, 0]]

C = B - A
print(C)
>>> [[-3, -1, 0], [1, 3, 0]]

neg_A = -A
print(neg_A)
>>> [[-4, -3, 0], [-2, -1, 0]]

C = 1 - Matrix([[4,3],[1,2]])
print(C)
>>> [[3, 3.0], [1.0, 1]]

 

행렬의 곱셈

행렬의 곱셈을 구현합니다. 1)  행렬에 스칼라를 곱하거나, 2) 행렬에 행렬 곱셈, 3) 두 벡터 간의 내적이 포함됩니다.

    def __rmul__(self, x):
        import copy
        M = copy.deepcopy(self)
        for r in range(M.nrows):
            for c in range(M.ncols):
                M[r,c] *= x
        return M

    def __mul__(self, A):
        if isinstance(A, (list, tuple)):
            return (self*Matrix(len(A), 1, fill=lambda r, c: A[r]))
        elif not isinstance(A, Matrix):  # in case of integer A
            return A*self  # __rmul__
        elif self.ncols == 1 and A.ncols == 1 and self.nrows == A.nrows:  # vector inner product
            return sum(self[r,0]*A[r,0] for r in range(self.nrows))
        elif self.ncols != A.nrows:
            raise ArithmeticError('incompatible dimension')

        M = Matrix(self.nrows, A.ncols)
        for r in range(self.nrows):
            for c in range(A.ncols):
                for k in range(self.ncols):
                    M[r,c] += self[r,k]*A[k,c]

        return M
        
     
A = Matrix([[4,3],[2,1]])
print(2*A)
>>> [[8, 6], [4, 2]]

print(A*A)
>>> [[22.0, 15.0], [10.0, 7.0]]

b = [2,1]
print(A*b)
>>> [[11.0], [5.0]]

b = Matrix([[1],[2],[3]])
print(b*b)
>>> 14

 


[알고리즘 & 코딩테스트/코딩테스트] - 파이썬으로 행렬 구현하기 (3) - 가우스 조던 소거법을 이용한 역행렬

[알고리즘 & 코딩테스트/코딩테스트] - 파이썬으로 행렬 구현하기 (4) - 마무리

반응형