Classes - Dealing with Complex Numbers

고등학교 때 배운 이후 기억 저편에 있던 복소수 개념을 다시 만나는 시간이 될 것이다. 간단한 복소수 연산법만 기억한다면 클래스 메소드 만드는 것은 응용수준이라 많이 어렵진 않았다.

문제 해석

Task

  • 두 복소수 C, D에 대하여 다음 연산을 수행하도록 클래스를 만들어라
    • C + D : addition (덧셈)
    • C - D : subtraction (뺄셈)
    • C * D : multiplication (곱셈)
    • C / D : division (나눗셈)
    • mod(C) 또는 mod(D) : modulus operations (복소수의 절대값)
  • 복소수는 real part(A)와 imaginary part(B)에 대해 A + Bi로 표현되며 다음과 같이 표현해야 한다.
    • B가 음수일 경우 +가 아닌 -로 표현
    • B가 0일 경우 A + 0.00i 로 표현
    • A가 0일 경우 0.00 + Bi 로 표현

Input Format

  • 공백으로 구분된 real part와 imaginary part 숫자 한 줄씩 두개

Output Format

  • 두 복소수 C, D에 대하여 위의 Task를 수행한 결과로 나온 복소수들 6개

주어진 코드 해석하기

1
2
3
4
5
6
7
8
9
10
11
12
13
14
import math

class Complex(object):
def __init__(self, real, imaginary):

def __add__(self, no):

def __sub__(self, no):

def __mul__(self, no):

def __truediv__(self, no):

def mod(self):
  • import math로 수학모듈을 소환한다. 아마 마지막에 제곱근을 구하는 sqrt() 함수를 써야해서 그런 것 같다.
  • Complex라는 클래스를 만들도록 뼈대만 구축해주었다. 각각을 살펴보자.
    • def __init__(self, real, imaginary) : 하단에 실행 코드를 보면 알겠지만 친젏한 HackerRank에서 이미 input을 받아 real과 imaginary 변수로 할당해주었다. 우리는 input의 형태에 신경쓸 필요 없이 복소수의 real part와 imaginary part를 각각 연산에 넣을 수 있도록 바로 instance variable로 할당해주기만 하면 된다.
    • def __add__(self, no) : 이전에 날 멘붕에 빠뜨렸던 Class 2 - Find the Torsional Angle 문제에서 익힌 매직메소드이다. 명령어로 __add__()를 쓰지 않더라도 +라는 연산자와 뒤에 오는 인자를 대상으로 해당 작업을 수행하게끔 하는 메소드이다. 이런 연산자 활용 매직메소드가 이번 문제에서 대거 출현하였으니 확실히 익히고 갈 수 있겠다.
    • def __sub__(self, no) : 위와 동일. 연산자가 -라는 것만 유의.
    • def __mul__(self, no) : 위와 동일. 연산자가 *라는 것만 유의.
    • def __truediv__(self, no) : 위와 동일하다. 연산자가 /라는 것만 유의.
    • def mod(self) : 복소수의 절대값을 구하는 것이므로 위의 연산들처럼 두개의 인자가 아닌 자신의 값만 인자로 주어진다.
1
2
3
4
5
6
7
8
9
10
11
12
13
def __str__(self):
if self.imaginary == 0:
result = "%.2f+0.00i" % (self.real)
elif self.real == 0:
if self.imaginary >= 0:
result = "0.00+%.2fi" % (self.imaginary)
else:
result = "0.00-%.2fi" % (abs(self.imaginary))
elif self.imaginary > 0:
result = "%.2f+%.2fi" % (self.real, self.imaginary)
else:
result = "%.2f-%.2fi" % (self.real, abs(self.imaginary))
return result
  • 이어 주어지는 매직메소드 __str__()는 instance의 imaginary 값이 0일 경우, real 값이 0일 경우, imaginary 값이 양수일 경우와 음수일 경우로 나누어 복소수 형태를 조정해주고 소수점 뒤 2자리까지 출력해주는 코드임을 알 수 있다.
    • 결국 결과값을 알맞은 복소수 형태로 만들어주는 보정에 대한 메소드인데, 이는 Task에서 연산 외에 요구했던 조건과 동일하다. 친절한 HackerRank가 다 해주었으니 연산 메소드만 만들관서 이 메소드를 return시 사용해주기만 하면 된다.
1
2
3
4
5
6
if __name__ == '__main__':
c = map(float, input().split())
d = map(float, input().split())
x = Complex(*c)
y = Complex(*d)
print(*map(str, [x+y, x-y, x*y, x/y, x.mod(), y.mod()]), sep='\n')
  • 마지막으로 실행 코드가 제공된다. input을 받아 float값으로 다 type casting을 해주고, 이를 Complex 클래스로 객체화 한 후 각 연산을 수행한 결과를 새로 줄바꿈하여 print 해주라는 실행명령이므로, 우리는 연산하는 코드만 제대로 짜서 결과값을 return 해주면 되겠다.

문제 풀이

복소수의 덧셈

  • 복소수의 덧셈은 real은 real끼리, imaginary는 imaginary끼리 더해준 값이기 때문에 상대적으로 간단하다.
    • no라는 인자 또한 Complex 객체여야만 연산이 가능하므로 이를 대상으로 해당 작업을 해주도록 메소드를 만들어준다.
    • 각 값을 합쳐 real_value와 imaginary_value라는 변수에 넣어주고 이에 __str__() 메소드를 사용한 값을 리턴했다.
1
2
3
4
def __add__(self, no):
real_value = self.real + no.real
imaginary_value = self.imaginary + no.imaginary
return Complex(real_value, imaginary_value).__str__()

복소수의 뺄셈

  • 복소수의 뺄셈은 덧셈과 동일하다.
1
2
3
4
def __sub__(self, no):
real_value = self.real - no.real
imaginary_value = self.imaginary - no.imaginary
return Complex(real_value, imaginary_value).__str__()

복소수의 곱셈

  • 복소수의 곱셈은 위의 두 연산보다는 아주 조금 복잡해진다. 하지만 아래 공식만 사용하면 어려울 건 전혀 없다.
    • a + bi, c + di 라는 복소수의 곱은 (ac-bd)+(ad+bc)i
      1
      2
      3
      4
      def __mul__(self, no):
      real_value = self.real*no.real - self.imaginary*no.imaginary
      imaginary_value = self.real*no.imaginary + self.imaginary*no.real
      return Complex(real_value, imaginary_value).__str__()

복소수의 나눗셈

  • 복소수의 나눗셈은 분모의 켤레복소수를 분모, 분자에 각각 곱한다는데, 이 결과는 아래 공식으로 구할 수 있다.
    • a + bi를 c + di 로 나눈 값은, ((ac+bd)+(bc-ad)i)/(c^2+d^2)
    • 즉 실수 부분은 (ac+bd)/(c^2+d^2), 허수 부분은 (bc-ad)/(c^2+d^2)이다.
      1
      2
      3
      4
      def __truediv__(self, no):
      real_value = (self.real*no.real+self.imaginary*no.imaginary)/(no.real**2+no.imaginary**2)
      imaginary_value = (self.imaginary*no.real - self.real*no.imaginary)/(no.real**2+no.imaginary**2)
      return Complex(real_value, imaginary_value).__str__()

복소수의 절대값

  • 마지막으로 복소수의 절대값을 mod()라는 함수로 만들어주어야 한다.
    • a + bi 라는 복소수의 절대값은 a^2+b^2의 제곱근이다.
    • Output Format을 보면 0.00i이라고 허수부분이 출력되어야 하므로 imaginary를 0으로 지정해준다.
      1
      2
      3
      4
      def mod(self):
      real_value = math.sqrt(self.real**2 + self.imaginary**2)
      imaginary_value = 0
      return Complex(real_value, imaginary_value).__str__()

느낀 점

이미 마련해준 공식만 있으면 클래스로 연산 만들기는 어렵지 않다. 매직메소드 원없이 써보면서 익혀서 좋다.
__str__은 객체를 출력할 때 자동으로 사용된다고 하던데, 이걸 self.__str__으로 입력하지 않고 다른 방법으로 실행하는 방법은 없을까 궁금.
복소수의 연산에 대해서 math 모듈이든 어디든 누군가 이미 만들어둔게 있지는 않았을까 하는 생각도.