C언어를 이용해서 산수를 처음부터 해보자. 오늘의 주제는 복소수로 사칙연산 하기다.
예제로 나온 코드는 전부 내가 직접 작성한 코드이다. 뭐 복사하나 새로 만드나 비슷비슷할테니 코드의 저작권을 따지지는 않도록 하겠다.
더불어, 어차피 복소수를 사용할 수 있는 방법은 아주 다양하고, 기존에 잘 쓰고 있는 방법이 많이 있을 거라고 생각한다. 내가 이걸 만든 이유는 배우는 것 보다 새로 짜는게 더 빠를 거라는 착각에 빠져서 만든 거였다. 다른 사람들은 그냥 기존에 있는 좋은 방법들을 사용하기를 바란다.

일단, 복소수가 어떤건지 먼저 정해줘야 한다.
//the type definition of complex number
typedef struct {
    double x;
    double y;
} cnumber;
typedef a b; 라고 쓰면 a라는 형식을 b라는 이름을 갖는 형식으로 쓰겠다는 뜻이다.
예를들어 typedef double merong; 으로 쓰면, 변수를 선언할 때 double a; 로 안하고 merong a; 로 해도 된다.
struct는 구조체를 선언한 것이고, cnumber라는 이름을 줬다. cnumber는 안에 x와 y를 멤버로 가지는데, 불러올때는 점을 찍거나 화살표 연산자로 불러오면 된다.

이제 덧셈을 해보자.
//The addition of two complex numbers z and w
cnumber add_cnum(cnumber z, cnumber w){
    cnumber a={z.x+w.x,z.y+w.y};
    return a;
}
일단, 이 함수가 하는 일은 두개의 복소수 z와 w를 받아서 복소수 값을 되돌려 주는 것이다. 그러므로 이 함수의 형식은 cnumber가 된다. 물론 cnumber는 앞에서 내부에 x와 y를 가지는 구조체형식이라고 미리 선언했으므로 쓸 수가 있다. 만약 이 선언을 안하게 되면 컴파일 할 때 "헛소리하지 마슈, cnumber라는게 대체 어딨냐?"고 에러를 내뱉는다.
내부적으로는 새로운 cnumber a를 선언해서 단순히 초기화 시킨다. 복소수의 덧셈은 그냥 실수부분과 허수부분을 각각 더해서 새로운 실수부분과 허수부분으로 나타내는 것이므로, 아주 간단하게 처리된다. 그리고 함수값을 되돌려 주기 위해서 return a;를 불러오면 된다.
사실 더 짧게 쓸 수도 있다.
//The addition of two complex numbers z and w
cnumber add_cnum(cnumber z, cnumber w){
   z.x+=w.x;
    z.y+=w.y;
return z;
}
여기서 += 이라는 연산자가 하는 일은, +=의 오른쪽에 있는 녀석을 왼쪽에 있는 녀석에게 더해준다는 뜻이다.
즉 a+=b라고 쓰는건 a=a+b라고 쓰는 것과 같다는 뜻이다. 이 +=연산자의 쓸모는 아주 많으므로 잘 알아두자. 물론 -=으로 쓴 것도 작동한다. 이 경우는 오른쪽에 있는 녀석을 왼쪽에 있는 녀석으로부터 빼게 된다.

덧셈을 잘 했으니, 뺄셈을 정의하려고 하는데, 뺄셈을 정의하려면 위와 마찬가지로 a.x=z.x-w.x로 해도 될 것이다. 하지만 뺄셈의 원래 정의인 a-b=a+(-b)를 실현하기 위해서 일단 복소수 z를 입력받아서 -z를 되돌려주는 함수를 만든다.
//The negative number of the given complex number z
cnumber minus_cnum(cnumber z){
    z.x=-z.x;
    z.y=-z.y;
    return z;
}
이 함수가 하는 일은 뻔하므로 설명하지 않겠다. 단지 =-는 -=가 아니라는 점만 주의하자.
그리고, -를 붙이는 함수를 굳이 만든 이유는, 앞으로 유용하기 때문이다.
//Subtracting w from z
cnumber sub_cnum(cnumber z, cnumber w){
    cnumber a;
    a=add_cnum(z,minus_cnum(w));
    return a;
}
드디어 뺄셈이다. 덧셈 함수를 실제로 응용한 함수가 될 것이다.
덧셈과 뺄셈을 잘 했다. 복소수는 그 자체로 체를 이루기 때문에 사칙연산이 모두 가능하다. 이제 사칙연산중 나머지 두개인 곱셈과 나눗셈을 정의해 주자.
//Multiplication of z by w
cnumber mul_cnum(cnumber z, cnumber w){
    cnumber a;
    a.x=z.x*w.x-z.y*w.y;
    a.y=z.x*w.y+z.y*w.x;
    return a;
}
복소수를 공부해본 사람은 알겠지만, 위의 함수가 복소수 z와 w를 받아서 새로운 복소수의 실수부분과 허수부분을 각각 정의하고 있다는 걸 알 수 있을 것이다.
문제는 나눗셈이다. 나눗셈을 정의하려고 하면 계산이 꽤 복잡해 지는데, 나눗셈 함수는 더 작은 함수로 쪼갤 수가 있다. 먼저, 켤레 복소수를 만드는 함수를 만든다.
//Complex conjugate of the given complex number z
cnumber conj_cnum(cnumber z){
    cnumber a;
    a.x=z.x;
    a.y=-z.y;
    return a;
}
비결은 -를 붙이는 함수랑 같다. 단지 허수 부분만 -가 붙어서 나온다는 점.
//The norm of the given complex number z
double norm_cnum(cnumber z){
    double a;
    a=sqrt(z.x*z.x+z.y*z.y);
    return a;
}
절대값을 되돌리는 함수도 만든다. 절대값은 실수이므로 double형이 된다. 그리고 애초에 난 이걸 z와 z의 켤레복소수를 곱해서 그 실수부분의 제곱근을 취하는 함수로 만들 생각이었는데, 만들고나니까 이렇게 되어 있었다.
//z is divided by w
cnumber div_cnum(cnumber z, cnumber w){
    cnumber a;
    a=mul_cnum(z,conj_cnum(w));
    a.x=a.x/(norm_cnum(w)*norm_cnum(w));
    a.y=a.y/(norm_cnum(w)*norm_cnum(w));
    return a;
}
이제 나눗셈을 정의할 수 있다. z를 w로 나누는데, z에 w의 켤레 복소수를 곱한 다음, w의 절대값의 제곱으로 나눠준다. 이 계산이 왜 나눗셈과 같은 계산인지는 직접 생각해 보면 되겠다.

이제 사칙연산을 모두 해봤으니, 복소수를 갖고 노는 다른 연산들을 정해볼 수도 있다. 가령, 위상각 돌리기가 된다.
//complex phase transformation of the given complex number z by k
cnumber phase_cnum(cnumber z, double k){
    cnumber x={cos(k),sin(k)};
    cnumber m=mul_cnum(z,x);
    return m;
}
복소수 위상phase은 크기1인 복소수를 곱해주는 것과 같고, 결국 그 실수부분은 cos함수로, 허수부분은 sin으로 표현되므로, k라디안만큼 돌리고 싶으면 cos(k), sin(k)를 성분으로 가지는 복소수를 곱해주면 된다.

이건 복소수의 위상각을 구해주는 함수이다. 위상각은 복소수를 극형식으로 표현했을 때 나오는 각도를 얘기하는데, 그냥 허수부분을 실수 부분으로 나누면 복소수의 절대값은 서로 약분되고, 탄젠트 함수 부분만 남게 된다. 따라서 각도를 구하려면 그 숫자의 아크탄젠트 값을 구하면 된다. 아크탄젠트는 C언어의 math.h에서 제공하므로, 아래의 함수를 쓰려면 #include<math.h>를 쓰고 컴파일 할 때 -lm옵션을 붙여야 할 것이다.
//The argument of the given complex number z
double arg_cnum(cnumber z){
    if(z.x==0.0){
        return PI/2.0;
    }
    else{
        double a=atan(z.y/z.x);
        return a;
    }
}
자, 간단한 복소수 사용법을 알아보았다. 정통 C에서 복소수를 어떻게 하는지는 모르겠고, C++은 다음 페이지에서 소개하는 것과 같이 아주 쉽게 되는 것 같다.
http://insar.yonsei.ac.kr/~tkhong/complex.html

by snowall 2007. 4. 26. 18:01