Complex numbers는 복잡하지 않아요.
이름만 좀 덜 놀랍게 지으면 될 뿐이에요.
복소수는 특히 공학과 과학에서 아주 유용해서, R은 integer, double, logical, character, raw와 나란히 복소수를 표준 원자형 타입으로 포함하고 있어요.
R에서 complex 수는 하나의 값이에요.
하지만 실제로 쓸 때는 두 수의 짝으로 생각할 수 있어요.
이 둘은 안타까운 역사적 이유 때문에 각각 "실수부"와 "허수부"라고 불려요.
다시 말하지만, 이상한 이름보다 그 바탕에 있는 단순함에 집중하는 게 좋아요.
두 실수로 복소수를 만들려면 허수부에 접미사 i만 붙이면 돼요.
2+3i
#> [1] 2+3i
typeof(2+3i)
#> [1] "complex"
실수 변수로 복소수를 만들 때는 위 문법이 통하지 않아요.
r + imi라고 쓰면 파서가 imi를 (존재하지 않는) 변수 이름으로 착각해요.
허수부에 1i를 곱하거나 [complex()][ref-complex] 함수를 사용해요:
r <- 2
im <- 3
r + im * 1i
#> [1] 2+3i
# must include the `real` and `imaginary` keywords
complex(real = r, imaginary = im)
#> [1] 2+3i
j가 아니라 왜 i일까요?
대부분의 엔지니어는 j를 선호해요.
대부분의 과학자와 수학자는 _허수(imaginary)_를 나타내는 수학 표기 i를 선호하지만, 그 표기는 전기공학에서 i가 _전류(current)_를 뜻하는 것과 충돌해요.
R은 i를, Python은 j를 선택했고, Julia는 im을 선택해 논란을 피했어요.
[STEM][wiki-STEM] 프로그래밍 언어 사이를 오갈 때는 주의해요.
복소수의 각 부분에 개별적으로 접근하려면:
z <- 2+3i
Re(z) # real part
#> [1] 2
Im(z) # imaginary part
#> [1] 3
어느 한쪽 부분이 0일 수도 있고, 그러면 수학자들은 그 수를 "완전히 실수" 또는 "완전히 허수"라고 말하기도 해요. 하지만 R에서는 여전히 복소수예요.
1.2 + 0i
#> [1] 1.2+0i
typeof(1.2 + 0i)
#> [1] "complex"
2i
#> [1] 0+2i
typeof(2i)
#> [1] "complex"
"i(또는 j)가 -1의 제곱근이다"라는 말을 들어봤을지도 몰라요.
지금은 허수부가 정의상 다음 등식을 만족한다는 뜻일 뿐이에요:
1i * 1i == -1 # more precisely, 1i * 1i == -1 + 0i
#> [1] TRUE
단순한 아이디어지만, 흥미로운 결과로 이어져요.
실수와 정수에 쓰는 대부분의 연산자는 복소수에도 그대로 통해서요:
z1 = 1.5 + 2i
z2 = 2 + 1.5i
z1 + z2 # addition
#> [1] 3.5+3.5i
z1 * z2 # multiplication
#> [1] 0+6.25i
z1 / z2 # division
#> [1] 0.96+0.28i
z1^2 # exponentiation
#> [1] -1.75+6i
2^z1 # another exponentiation
#> [1] 0.518895+2.780422i
대부분의 수학 함수는 복소수 입력에서도 동작해요.
Re()와 Im() 외에도 복소수와 특히 관련이 깊은 함수가 몇 가지 있어요.
Conj()는 복소수 허수부의 부호를 뒤집기만 해요(+에서 -로, 또는 그 반대로).
abs(<complex number>)는 허수부가 없는 실수(그 수의 "크기")를 반환하는 것이 보장되고, Mod(<complex number>)는 같은 뜻이에요.Arg(<complex number>)는 위상각을 라디안으로 반환해요.z1
#> [1] 1.5+2i
abs(z1)
#> [1] 2.5
Mod(z1) # same as previous
#> [1] 2.5
Conj(z1) # flip sign
#> [1] 1.5-2i
Arg(z1)
#> [1] 0.9272952
이미 포화 상태인 시장에서 다른 서비스와 차별화하려는 새로운 택시 서비스에 막 입사했어요. 이를 위해 CEO는 맨해튼에서 초호화 서비스를 제공하기로 했어요. 모든 교차로에 안내원을 두어 승객이 타기 전과 내린 후를 돕는 방식이에요. 이제 이 서비스의 운영을 돕는 코드를 작성해야 해요.
CEO는 맨해튼의 도로가 주로 격자 모양이라는 점에서, 복소수가 거리 교차로를 나타내는 편리한 방법이라고 결정했어요. 여기서 양수와 음수 실수는 각각 동쪽과 서쪽을, 양수와 음수 허수는 각각 북쪽과 남쪽을 나타내요.
R에서 복소수를 만드는 방식은 좀, 음... 독특하기 때문에, 간단한 복소수 함수를 구현하라는 지시를 받았어요.
정수 a와 b를 받는 simplex(a, b) 함수를 구현해요.
복소수 a+bi를 반환해요.
simplex(1, 1)
#> 1+1i
simplex(-3, 4)
#> -3+4i
고객이 목적지 교차로를 알려주면, 운전기사는 거기까지 가는 방법에 대한 안내를 받아요. 이 안내는 복소수 형태로 주어지는데, 실수부는 북쪽/남쪽으로 몇 블록 가야 하는지를, 허수부는 동쪽/서쪽 거리를 나타내요. 표준 나침반에 맞추기 위해, 프로젝트 매니저는 북쪽을 양의 허수 방향으로, 남쪽을 음의 허수 방향으로 정했어요. 마찬가지로 동쪽은 양의 실수 방향이고, 서쪽은 음의 실수 방향이에요.
출발 교차로 좌표 start와 목적지 좌표 end를 받는 driver_directions(start, end) 함수를 구현해요.
운전기사가 동쪽/서쪽으로, 그리고 북쪽/남쪽으로 몇 블록 가야 하는지를 나타내는 복소수를 반환해요.
driver_directions(0+0i, 7+5i)
#> 7+5i
driver_directions(-2+3i, 4-5i)
#> 6-8i
고객은 택시가 이동해야 하는 거리에 대한 요금을 내요. 맨해튼의 도로는 격자 모양이라서, 택시는 이 경로를 따라서만 다닐 수 있어요. 이런 구조 때문에 격자 선을 따라가야만 하는 거리 측정 방식이 생겨요. 이 거리는 지나간 선의 개수나 도착한 교차로의 개수(즉, 출발 교차로는 세지 않아요)를 더해서 구할 수 있어요. 이 측정값을 흔히 맨해튼 거리 또는 L1 노름이라고 불러요.
start와 end 교차로를 받는 manhattan(start, end) 함수를 구현해요.
start와 end 교차로 사이의 맨해튼 거리를 반환해요.
manhattan(0+0i, 3+4i)
#> 7
manhattan(-2-3i, -1+1i)
#> 5
CEO는 이 서비스가 초호화일 뿐만 아니라 친환경적이라고 강조해서 투자 은행들을 설득했어요. 교차로에 있는 안내원들 사이에서 메시지를 나르는 데 훈련된 까마귀를 쓸 거라서요(안내원들은 맞춤형 경험을 위해 승객 정보를 전달해요). 투자 은행가들은 이것이 도대체 왜 환경 친화적인지, 심지어 어떤 CEO가 여가 시간에 까마귀 훈련을 즐기는지 알아보려고 실사를 할 생각을 전혀 하지 않은 것 같아요. 왜냐하면 투자금은 계속 밀려들고 있으니까요.
이 서비스도 이동한 거리에 따라 요금을 받아요. 하지만 까마귀는 날 수 있어서 격자 도로망을 따를 필요가 없고 최단 경로로 갈 수 있어요. 물론 거대한 고층 빌딩이 있으면 얘기가 다를 수 있지만, CEO는 까마귀가 낮은 건물 위를 쉽게 날아갈 수 있는 캔자스주 맨해튼(일명 "작은 사과")에 이 서비스를 넣으려고 해요. 캔자스주 맨해튼에서 이런 서비스에 대한 잠재 수요가 얼마나 될지, 그리고 이 많은 새가 머리 위를 날아다니는 것을 지역 보행자들이 어떻게 느낄지는 나중에 생각할 문제예요.
start와 end 교차로를 받는 as_crow_flies(start, end) 함수를 구현해요.
두 교차로 사이의 까마귀가 날아가는 거리를 반환해요.
as_crow_flies(0+0i, 3+4i)
#> 5
CEO는 까마귀에게 운전기사와 비슷한 종류의 방향을 따르도록 훈련시켰어요. 하지만 CEO는 남반구를 매우 좋아해서, 프로젝트 매니저가 시스템 나머지 부분에 쓰는 나침반과는 다른 나침반으로 까마귀를 훈련시켰어요. CEO는 꽤 괴짜인 것 같아요. CEO의 나침반은 북쪽이 음의 허수 방향이고 남쪽이 양의 허수 방향이에요. 이는 운전기사의 북쪽/남쪽 방향과 반대이고, 동쪽/서쪽은 그대로예요.
start와 end 교차로를 받는 crows_directions(start, end) 함수를 구현해요.
운전기사의 방향과 비슷하지만 북쪽/남쪽이 뒤집힌 복소수를 반환해요.
driver_directions(0+0i, 7+5i)
#> 7-5i
driver_directions(-2+3i, 4-5i)
#> 6+8i
Exercism에 가입하고 R 트랙을 개념 21개연습 문제 111개, 그리고 실제 사람의 멘토링과 함께 배우고 익혀 보세요. 모두 무료예요.