필립 클라인의 저서 코딩 더 매트릭스 2장 필드 중 복소수
- 복소수와 복소 평면을 알아봅니다.
- 복소수의 연산이 복소 평면에서 어떻게 표현되는지 알아봅니다.
- 오일러의 공식을 이용해 복소수를 편각으로 표현하고 복소 평면에 표현해 봅니다.
복소수
모든 실수에 대해 인 경우는 없습니다. 수학자들은 이 문제를 해결하기 위해 -1의 제곱근 를 도입합니다. 그리고 어떤 실수에 를 곱한 수를 허수(imaginary number)라고 부릅니다. 복소수는 허수와 실수의 합, 예를 들면 같은 수입니다. 고등학교에서 배운 수학을 조금 더 떠올려 보면 다음과 같은 식도 있었습니다.
와 는 서로 켤레 복소수입니다.
Python에서 허수 는 1j
와 같이 씁니다. 예를 들어 -9의 제곱근 는 3j
라 씁니다. 아주 간단한 연산을 예를 들면
>>> (1 + 3j) + (10 + 20j)
(11 + 23j)
>>> x = 1 + 3j
>>> (x - 1) ** 2
(-9+0j)
>>> y = 1 - 3j
>>> x * y
(10+0j)
복소수의 덧셈, 뺄셈은 실수는 실수끼리 허수는 허수끼리 연산을 수행합니다. 곱셈은 인수분해된 두 일차식을 곱해 2차식을 만들듯 각항의 곱들을 더한 값입니다. 복소수의 나눗셈은 제곱근을 포함하는 유리수 분모의 실수화와 비슷합니다. 를 계산하기 위해 분모, 분자에 를 곱하는 것처럼 복소수의 경우 아래와 같이 분모를 실수화해 계산합니다.
복소수 필드
복소수 필드 또는 복소 평면은 복소수가 실수부와 허수부 두 개의 독립된 변수로 이루어 진다는 점을 이용해 만들어집니다. 복소 평면의 축은 실수부, 축은 허수부입니다.
Python에서는 변수 z
가 복소수일때 z.real
z.imag
로 실수부와 허수부를 분리합니다. 실제 좌표계를 띄워 눈으로 보고 이해하는게 가장 좋을 것 같습니다. 코딩 더 매트릭스에서는 소스파일을 제공해 독자들의 실습을 돕고 있습니다. 좌표를 직접 띄울 수 있도록 plotting.py
모듈 파일을 제공하니 유용하게 쓸 수 있을 것 같습니다. 다음 코드는 plotting.py
의 코드 중 일부입니다.
for pt in L:
if isinstance(pt, Number):
x,y = pt.real, pt.imag
else:
if isinstance(pt, tuple) or isinstance(pt, list):
x,y = pt
else:
raise ValueError
h.writelines([
'<circle cx="%d" cy="%d" r="%d" fill="red"/>\n'
% (origin[0]+scalar*x,origin[1]-scalar*y,dot_size)
])
h.writelines(['</svg>\n</body>\n</html>'])
리스트로 제공될 복소수들을 HTML의 svg
태그로 매핑하는 반복문입니다. 함수를 호출하면 브라우저 화면에 좌표축을 그리고 전달한 복소수의 리스트를 점으로 표시하게 됩니다. 예를 들어 다음과 같은 복소수 리스트 는 좌표상에 아래와 같이 그려집니다.
L = { 2 + 2j, 1.5 + 1j, 2 + 1j, 2.5 + 1j }
plot(L)
복소수의 절대값
복소수의 절대값이란 복소 평면의 원점에서의 거리를 말합니다. 2차원 벡터의 거리처럼 피타고라스의 정리로 구할 수 있습니다. 표기는 , Python에서는 abs(z)
로 표현합니다. 아래 그림에서는 파란 화살표의 길이가 절대값입니다.
켤레복소수를 알면 제곱을 쓰지않고 복소수의 절대값을 구할 수 있습니다. 허수 이 -1이라는 성질을 이용하면 됩니다. 일때 다음 식이 성립합니다.
복소수 연산과 필드
각각의 요소에 를 더하면 복소 평면에서 어떻게 그려질까요? 복소수의 덧셈은 좌표에서 평행이동 변환을 나타냅니다.
L = { 2 + 2j, 1.5 + 1j, 2 + 1j, 2.5 + 1j }
plot({ x + (1 + 2j) for x in L })
원본 와 평행이동
벡터에 스칼라곱을 하듯 복소수에 실수를 곱하면 크기 변환을 할 수 있습니다. 복소수 에 2를 곱하면 각각의 점들은 원점과 서로에게 두배 멀어지고 를 곱하면 두배 가까워집니다. 만약 곱하는 실수가 음수라면 180도 회전 후 스케일링한 결과를 보입니다.
plot({ x * 0.5 for x in L })
plot({ x * -0.5 for x in L })
원본 와 0.5 스케일링, -0.5 스케일링
평행이동과 스케일링은 벡터와 동일한 연산으로 나타낼 수 있지만 복소수에 한해 허수 를 곱함으로서 90도 회전을 나타낼 수 있습니다. 이 성질은 90도 회전이 라는 점을 이용한 것입니다. 에 를 곱하면 가 됩니다. 이는 복소 평면에서 가 서로 바뀐 후 에 을 곱한 것과 같습니다. 90도 회전 변환을 나타내기 위해 예시로 이미지를 사용해 보겠습니다. 아래 코드는 코딩 더 매트릭스 소스파일에서 제공하고 있는 파일을 흑백 명암처리하는 프로시저입니다.
def get_pts():
data = file2image('img01.png')
pts = {
(x + y*1j) * 0.02 # 이미지의 크기를 plot 함수의 좌표 범위에 조정 (* 0.02)
for y, row in enumerate(reversed(data)) # 상하 반대
for x, rgb in enumerate(row)
if rgb[0] < 256 * 0.5 # 128 미만은 검은색, 이상은 흰색
}
return pts
S = get_pts()
plot(S)
90도 회전 변환을 위해 원래 수에 곱 매핑하는 컴프리헨션을 이용합니다.
plot({x * 1j for x in S})
복소 평면에서의 편각
복소 평면에서 단위원에 대한 호도를 편각이라 합니다. 실수부축 절편 1과 -1에서 각각 0, , 허수부축 절편 1, -1에서 각각 , 입니다. 오일러는 복소수 를 편각으로 표현하는 공식을 만들었습니다. 점 의 편각은 입니다. 를 오일러의 공식에 대입하면 을 얻습니다. 아래 코드는 를 Python으로 계산한 결과입니다.
>>> from math import pi, e
>>> e**(pi * 1j)
(-1+1.2246467991473532e-16j)
매우 작은 허수부를 고려할때 결과가 -1임을 보여줍니다. 다음 코드는 값을 0에서 씩 더해가며 복소수 집합을 만들고 이를 좌표에 나타냅니다.
from math import pi, e
plot({ e ** (1j * pi * (1 / 4) * r) for r in range(0, 8)})
오일러의 공식을 이용해 단위원 뿐만 아니라 복소 평면 위의 모든 점을 편각으로 표현할 수 있습니다. 복소 평면 위의 임의의 점 가 있을때 원점에서 까지의 선분이 단위원과 만나는 지점, 가 있습니다. 원점에서 까지의 거리가 일때 와 의 원점으로부터 거리의 비율은 입니다. 따라서 이라 쓸 수 있습니다. 한편 은 단위원 위의 점이므로 오일러 공식에 의해 입니다. 정리하면 다음과 같은 간단한 식을 쓸 수 있습니다. 여기서 과 를 의 극좌표라 합니다.
복소 평면 위의 임의의 점 가 있을때 이 점을 만큼 회전시킨다면 다음과 같이 쓸 수 있습니다.
위의 식을 이용해 앞서 복소 평면에 그려봤던 이미지를 만큼 회전시켜 봅시다.
def rotate(pt, radian):
return pt * e **(radian * 1j)
S = get_pts()
plot({rotate(x, pi / 4) for x in S})
만약 이미지를 만큼 회전시킨 후 배 스케일링, 평행 이동해 이미지의 중심이 원점으로 가게 하려면 어떻게 해야 할까요? 각각의 목적이 독립적이므로 각각을 함수로 작성하면 좋을 것 같습니다.
from math import e
def rotate(pt, radian):
return pt * e **(radian * 1j)
def scale(pt, num):
return pt * num
def move(pt, c_num):
return pt + c_num
이미지의 중심이 원점으로 가야한다면 현재 이미지의 중심을 알아야 합니다. 중심은 각각의 축에 대해 최대값과 최소갑을 더한 후 2로 나누면 구할 수 있습니다. Python의 내장함수 max
와 min
을 이용하면 좋습니다.
middle_of_real = (max(reals) - min(reals)) / 2
middle_of_imag = (max(imags) - min(imags)) / 2
이미지의 중심 좌표를 각각의 점에서 빼면 원점으로 이동합니다. 각각의 함수를 합성해 복소수의 집합을 새롭게 구성합니다.
from math import pi
S = get_pts() # origin
S_result = { rotate(scale(move(x, -1 * (middle_of_real + middle_of_imag * 1j)), 1/2), pi / 4) for x in S }
plot(S_result)