第14回 プログラミングについて 『複素数の話』

『複素数の話』
複素数の話。皆さんは複素数というものを知っていますか。多分理科系の方ならどなたでも知っていると思います。
知らない方のためにちょっとだけお話をしましょう。そもそも複素数というものはどうして誕生したかと申しますと、平方根(いわゆるルートというものです)を求めるときに求められない数があることがその根源となっているのです。どんな数が求められないかといえば、負の数です。以下ルートを示すのにSQRT()と記述します。
SQRT(-1)
は通常は答えが求められないのです。いまある数 N があったとき N と N をかけ合わせて負になる数などというものは自然界には存在しないのです。もっともゼロや負の数も自然界には存在しませんので少し解釈を拡大して、負の無限大から正の無限大までのすべての数としましょう。
ところが代数学的に考えてみると平方根の自乗は元の数なのですから、
SQRT(-1)× SQRT(-1)=-1
ということにります。当たり前なことですね。となると負の数それも-1の平方根は存在していることになってしまうのです。そこで過去の偉大な数学者はこのままでは代数の計算ができないで、便宜的に自乗すると-1になる数を作ることにしました。考えてみれば、かなりいい加減な話なのですがこの数のことを虚数というのです。算数の世界では、IMANGEという意味から i と表現します(1と間違えやすいので j とも書きます)。
i×i=-1
なのですから、
SQRT(-1)=i
ということになりますね。
それでは、1+iはいったいいくつになるのでしょうか。それはこれ以上計算ができないのでやっぱり1+iのままなのです。この通常の数(実数)と架空の数(虚数)で構成された数を複素数と呼ぶのです。
参考に複素数と複素数の演算を簡単に考えてみましょう(なんだか算数講座みたいですね)。
加算:
(1+i)+(1+i)=(1+1)+(i+i)=2+2i
減算:
(1+i)-(1+i)=(1-1)+(i-i)=0+0i=0
乗算:
(1+i)×(1+i)=(1+i)×1+(1+i)×i
=(1+i)+(1×i+i×i)
=(1+i)+(i+(-1))
=(1+i)+(i-1)
=(1-1)+(i+i)
=0+2i
=2i
(1+i)×(1-i)=(1+i)×1+(1+i)×(-i)
=(1+i)+(1×(-i)+i×(-i))
=(1+i)+(-i-(i×i))
=(1+i)+(-i-(-1))
=(1+i)+(-i+1)
=(1+1)+(i-i)
=2+0
=2
∴1+iに対して1-iは共役複素数と呼びます。
除算:
(1+i)÷(1+i)=1 (同じもので割ると当然1です)
(1+i)÷(1-i)は分数で表現すると、
(1+i)
---------- となり、分母の数の共役複素数(1+i)を分母と分子にかけると、
(1-i)
(1+i)×(1+i) 2i
----------------------=------=i
(1-i)×(1+i) 2
となります。
さてこの複素数を2次元のXY座標系(直角座標系)で考えてみると、Xを実数とするとちょうどYが虚数に対応するのです。ということは複素数はXY座標系の演算に使えるということです。さらに思い出してみると、この複素数はこの座標系ではベクトルということになります。ベクトルという言葉を聞くとアレルギーのでる方もいるかと思いますが、図形を扱うプログラムを書くには避けて通れないので慣れておくことをおすすめします。
いま任意の点Pを(X、Y)とします。これはちょうど X+Yi に相当することは察しがつくでしょう。座標原点からのベクトルとも考えられます。このベクトルの絶対値は、SQRT(X×X+Y×Y)となりますので、これはベクトルの長さに相当します。
またベクトルは三角関数で表現すると、
L(cos(A)+sin(A)i)
ここで、Lはベクトルの長さ、Aはベクトルの角度。
とも表現することができます。そこで2つの複素数 X1+Y1i と X2+Y2i の除算をみてみましょう。
X1+Y1i を L1×(cos(A1)+sin(A1)i)、
X2+Y2i を L2×(cos(A2)+sin(A2)i)
と表現して計算をすると、
X2+Y2i L2×(cos(A2)+sin(A2)i)
------------=------------------------------------------
X1+Y1i L1×(cos(A1)+sin(A1)i)
式が長くなってしまいますので、cosをc、sinをsとします。
L2 (c(A2)+s(A2)i)×(c(A1)-s(A1)i)
=----×--------------------------------------------------------
L1 (c(A1)+s(A1)i)×(c(A1)-s(A1)i)
分母はc(A1)×c(A1)+s(A1)×s(A1)となり、これは三角関数では1となりますので、
L2
=----×(c(A2)+s(A2)i)×(c(A1)-s(A1)i)
L1
の計算となります。括弧の中は、
c(A2)×c(A1) +s(A2)×c(A1)i
-c(A2)×s(A1)i+s(A2)×s(A1)
となり、実数部と虚数部を整理すると、
(c(A2)×c(A1)+s(A2)×s(A1))
+(s(A2)×c(A1)-c(A2)×s(A1))i
実数部は三角関数の定理を利用しすると、
(c(A2+A1)+c(A2-A1))/2
+(c(A2-A1)-c(A2+A1))/2
となり、整理すると、
c(A2-A1)
となります。虚数部は
(s(A2+A1)+s(A2-A1))/2
-(s(A2+A1)-s(A2-A1))/2
となり、整理すると、
s(A2-A1)
となります。結果として、
X2+Y2i L2
------------=----×(cos(A2-A1)+sin(A2-A1)i)
X1+Y1i L1
となります。この式が何を意味しているかといえば2つのベクトルの関係を示しているのです。端的に言えばベクトルとベクトルのなす角度の成分を示していると考えられます。実は、私は直線と点や直線と直線のとの関係を簡単に求めたいときにはこのベクトルの性質を利用することがあります。
プログラミング言語でこの複素数を言語の仕様の中に取り入れてあるのは、私の記憶のかぎりではFORTRANのみだったと思います。FORTRANでは
COMPLEX A,B,C
A=(1,1)
B=(2,3)
c=A+B
というように通常の変数と同じように使用することができます。
しかしながらC言語では複素数は言語仕様のなかにはありませんので、別の形で表現しなければなりません。C言語では構造体を使って次のように表現します。
struct complex_strct{
float x,y;
};
struct complex_strct a,c,b;
a.x=1;
a.y=1;
b.x=2;
b.y=3;
c.x=a.x+b.x;
c.y=a.y+b.y;
などとできますが、複素数の演算は複素数専用の関数で実行させた方が便利ですので、
struct complex_strct cmplxcpy();
struct complex_strct cmplxadd();
a=cmplxcpy(1,1);
b=cmplxcpy(2,3);
c=cmplxadd(a,b);
とし、これらの関数を
struct complex_strct cmplxcpy(x,y)
float x,y;
{
struct complex_strct t;
t.x=x;
t.y=y;
return t;
}
struct complex_strct cmplxadd(a,b)
struct complex_strct a,b;
{
struct complex_strct t;
t.x=a.x+b.x;
t.y=a.y+b.y;
return t;
}
などとします。
C++では演算子のオーバーロードの機能(演算を行なう値の型に対して個別に演算方法を定義できる機能)がありますので、FORTRANと同様な式で表現ができますがここではそれについての詳細は省略します。
一見あまり使いようのない複素数なのですが、それを色々と調べてみると図形の処理を行なうときには便利なことがあります。暇があるときには研究してみるのも面白いと思います。そして画期的な使い方があったら是非私に教えて下さい。
それではまた次回。