2008. 4. 21.
石立 喬

Visual C++ 2008 Express Edition の易しい数値計算(8)

――― Taylor展開の計算と、交流理論の簡単な複素数計算 ―――

 過渡的でない、定常状態での電気回路の計算には、交流理論が便利である。しかし、交流理論では複素数がでてきて、初心者には分かりにくい。そこで、電気回路と複素数の関係に馴染めるように、まずTaylor展開の数値計算を紹介する。次に、電気回路に余り詳しくない者のために交流理論を簡単に説明し、複素数ライブラリによる計算を行う。波形の表示をさせないので、コンソールアプリケーションを使用する。
 数学では、虚数をi(imaginaryの意)で表すが、電気の世界では、電流iと紛らわしいのでjで表す習慣があり、ここでは、それに従う。

Taylor展開(Expansion)
 英国の数学者Brook TaylorによるTaylor展開は、関数を多項式に展開するもので、関数が何回でも繰り返し微分可能であることが条件になっている。多項式の各項は級数であり、Taylor級数とも呼ばれる。Taylor展開の特殊なケースとして、Maclaurine展開があるが、Taylor展開と総称されることも多い。
 良く知られているTaylor展開の例を以下に示す。

 特にx=0の場合には、

となり、e(自然対数の底)の値を求めることができる。 この他に、三角関数、双曲線関数もある。これらは、xのすべての値に対応できる。

 Taylor展開で見ると、ex、sinx、cosx、sinhx、coshx は全部兄弟のように見える。
exの代わりにejxとおいてTaylor展開をすると、

となり、j2=-1 であるので、

となって、オイラーの公式が得られる。この意味については、後述する。

Taylor展開の数値計算
 これには、コンピュータが最も得意とする繰り返しループを使用する。あらかじめ繰り返し回数が決まっている(加算する項数を決めておく)計算にはforループが適しているが、ここでは、加算して行く項の値が一定以下になれば打ち切る、後判定型の do〜while ループを使用する。
 従来の数値計算法の教えによれば、大きい値に、次第に小さい値を加算して行くのは、情報落ち誤差が発生するとして推奨されず、小さい方から、次第に大きい値を加算して行く決まりがある。今回、その確認実験を行ったが(内容は省略)、実用範囲の桁数では結果が完全に一致し、誤差が発生しないことが分かったので、単純な方法をとった。

Taylor展開の計算方法
 Taylor展開の計算に当たっては、級数の各項(第i項、i=0から始まる)をpiとし、piはpi-1から求めるようにする。第0項は、そのままsumの初期値とし、i=1から加算を始める。
 プログラムで表すと、
   sum=[sumの初期値、すなわち第0項];
   p=[pの初期値、すなわち第0項];
   i=1;
   do{
      p*=[pの倍率、すなわちpi/pi-1];
      sum+=p;
      i++;
   }while(Math::Abs(p)>[許容誤差]);
のようになる。

を例にとると、第0項は1であり、したがってsumおよびpは1に設定する。第i項を第(i-1)項から求めるには、x/iを掛ければよいので、p*=x/iとする。
 前記の各関数についてまとめると、表1のようになる。

表1 いろいろな関数のTaylor展開をプログラム化するための設定条件

  ex sinx cosx sinhx coshx
sumの初期値、pの初期値  1  x 1 x 1
比率pi/pi-1 x/i -x2/(2i(2i+1)) -x2/((2i-1)2i) x2/(2i(2i+1)) x2/(2i-1)2i)


Taylor展開を計算するプログラム

#include "stdafx.h"

using namespace System;

int main()
{
   Console::Title="Taylor展開の計算";

   double sum,p,x;
   int i;
   String^ string1;

   // --------------------- exp(1.0)の計算----------------------
   x=1.0;
   sum=1.0;
   p=1.0;
   i=1;
   do{
      p*=x/i;
      sum+=p;
      i++;
   }while(Math::Abs(p)>1.0E-15);
   string1=String::Format("所要項数:{0,2:D} exp(1.0)={1}",i,sum);
   Console::WriteLine(string1);

   // --------------------- sin(π/2)の計算---------------------
   x=Math::PI/2;
   sum=x;
   p=x;
   i=1;
   do{
      p*=-x*x/(2*i*(2*i+1));
      sum+=p;
      i++;
   }while(Math::Abs(p)>1.0E-15);
   string1=String::Format("所要項数:{0,2:D} sin(π/2)={1}",i,sum);
   Console::WriteLine(string1);

   // --------------------- cos(π/4)の計算---------------------
   x=Math::PI/4;
   sum=1.0;
   p=1.0;
   i=1;
   do{
      p*=-x*x/((2*i-1)*2*i);
      sum+=p;
      i++;
   }while(Math::Abs(p)>1.0E-15);
   string1=String::Format("所要項数:{0,2:D} cos(π/4)={1}",i,sum);
   Console::WriteLine(string1);
   Console::WriteLine();
   Console::WriteLine("参考");
   Console::WriteLine("Math::E=" + Math::E);
   Console::WriteLine("Math::Sin(Math::PI/2)=" + Math::Sin(Math::PI/2));
   Console::WriteLine("Math::Cos(Math::PI/4)=" + Math::Sin(Math::PI/4));

   Console::ReadLine();

   return 0;

}


得られた結果
 得られた結果を図1に示す。参考までに表示した、組み込み関数の結果と良く一致している。


図1 得られた結果


オイラーの公式(Euler’s Formula)から交流理論へ
 図2に示すように、ejxは半径1の円周に沿って時間とともに反時計方向に回っている極座標上の点であり、xは実数軸から測った偏角である。これを、横軸が実数で、縦軸が虚数の直角座標上で表したのが、cosx+jsinxであると考えられる。
 時間とともに変化する三角関数は、ejxを右横から観察したものである(虚数軸へ投影した軌跡)。なぜ右横からか?なぜ虚数軸なのか?は、単に、時間0で0であるサイン関数の方が良いからに過ぎない。


図2 オイラーの公式の意味


 図2から明らかなように、jは位相をπ/4だけ進める演算子であると考えられる。sinxの頭にjを付けると(sinxをj倍すると)、それはsin(x+π/4)すなわちcosxになる。
 jで割ることは、位相をπ/4だけ遅らせることであると考えられる。sinxをjで割ると、それはsin(x-π/4)すなわち-cosxになる。
 偏角xの代わりに、角周波数をωとし、時間をtとして、x=ωtを用いると、時間tで微分することはjω倍することであり、積分することはjωで割ることになる。

電気回路素子の基礎
 電気回路素子に抵抗R、コイルL、コンデンサCがあることは知っていることと思う。しかし、これらの呼び方は正確ではない。英語で表現すると明確になるので、英語とともに日本語での呼び方を表2に紹介する。

表2 電気回路素子の名称と値

略称 素子の名称 素子に固有の値 電気的な値
R Resistor
抵抗(器)
Resistance
抵抗
単位はオーム(Ω)
Resistance
抵抗
単位はオーム(Ω)
L Inductor
コイル、インダクタ
Inductance
インダクタンス、誘導
単位はヘンリー(H)
(Inductive)Reactance
(誘導性)リアクタンス
単位はオーム(Ω)
C Capacitor
コンデンサ、キャパシタ
Capacitance
容量
単位はファラッド(F)
(Capacitive)Reactance
(容量性)リアクタンス
単位はオーム(Ω)


 「コイル」は形状を表したものに過ぎず、「コイルばね」は機械部品にもある。「コンデンサ」は、凝縮器の意味で、空調機などに使われている。ドイツでも「Kondensator」と言うので、日本の電気技術がドイツから入ってきた名残かもしれない。なるべく、「インダクタ」、「キャパシタ」を使用したいものである。略称で、インダクタやインダクタンスをLで表すのは、電流にIが使われているからで、ドイツの物理学者LenzのLを取ったと言われる。

交流理論の考え方
 電圧や電流が定常的で、その波形が正弦波、または正弦波の集まりである場合、非常に便利な計算方法があり、その理論を交流理論と呼ぶ。
 直流回路で、電圧v、電流i、抵抗Rの間に v=Ri の関係があるのがオームの法則(Ohm’s Law)であるが、これを交流回路に拡張して、電圧v、電流i、インピーダンスZの間にも v=Zi の関係を持たせるのが交流理論である。この場合、v、i、Zは一般的に複素数である。インピーダンス(Impedance)の単位は、抵抗と同じくオーム(Ω)である。
 インピーダンスZは、これを複素数表示して
   Z=R+jX
とする。Rは抵抗(Resistance)、Xはリアクタンス(Reactance)である。
 素子を流れている正弦波電流の角周波数をωとすると、インダクタンスLを有するコイルに対しては、誘導性リアクタンス X=ωL であり、値が正である。また、容量Cのコンデンサに対しては、容量性リアクタンス X=-1/(ωC) であり、値は負である。
 抵抗RとコイルLの直列回路に電流i=Imsinωt が流れているとすると、直列回路の両端の電圧vは、
   v=Ri+Ldi/dt=RImsinωt+ωLImcosωt
であり、先に述べたように、 cosωt=j x sinωt の関係を利用すると、
   v=RImsinωt+jωLImsinωt
    =(R+jωL)Imsinωt
    =(R+jωL)i
となり、Z=R+jωL とすると、v=Zi の形に書くことができ、直流回路のオームの法則と同じ形になる。

 抵抗RとコンデンサCの直列回路に電流i=Imsinωt が流れているとすると、直列回路の両端の電圧vは、
   v=Ri+1/C∫idt=RImsinωt-1/(ωC)Imcosωt
であり、先に述べたように、 -cosωt=sinωt /j の関係を利用すると、
   v=RImsinωt+1/(jωC)Imsinωt
    =(R+1/(jωC))Imsinωt
    =(R-j/(ωC))Imsinωt
    =(R-j/(ωC))i
となり、Z=R-j/(ωC) とすると、v=Zi の形に書くことができる。

複素数演算ライブラリ(complexクラス)の使い方
 標準C++言語のcomplexクラスライブラリの使用方法については、すでに、「Visual C++ 2005 Express Editionの易しい使い方(11)」で述べてあるが、再度簡単に紹介する。
1) complexクラスを使用するには、
   #include <complex>
   using namespace std;
を記述しておく。
2)complexクラスはテンプレートクラスなので、型をパラメータとして与える。具体的には、
   complex<double> z;
   complex<float> z;
 などで宣言する。同時に値を与えて初期化する場合には、
   complex<double> z(3.0,4.0);
 が用いられる。
3)すでに宣言されているcomplexクラスのオブジェクトに対しては、
   z=complex<double>(3.0,4.0);
 または、
   z.real(3.0);
   z.imag(4.0);
で実数部と虚数部を設定する。
4) complexクラスのオブジェクトから実数部や整数部を取り出すには、
   real1=z.real();
   img1=z.imag();
を用いる。
5) complexクラス間の四則演算の他、絶対値(abs)、偏角(arg)、三角関数(sin,cos,tan)、双曲線関数(sinh,cosh,tanh)、平方根(sqrt)、冪(exp,pow)、対数(log,log10)などの関数を使用できる。

交流理論の簡単な例題
1)200Ωの抵抗と1Hのコイルが直列に接続され、そこに周波数50Hzで100Vの電源が接続されている。流れる電流はいくらか?電圧に対して電流の遅れ角はいくらか?
2)50Ωの抵抗と5μFのコンデンサが直列に接続され、そこに周波数1kHzで10Vの電源が接続されている。流れる電流はいくらか?電圧に対して電流の進み角はいくらか?
注:電流は、電圧をインピーダンスで割ったものである。したがって、インピーダンスの虚数成分(すなわち、リアクタンス成分)が正のときは、電流の虚数成分は負になり、偏角で言うと「遅れ」になる。コイルのように、インピーダンスの虚数成分が正になる素子は、電流を遅らせ、コンデンサのようにインピーダンスの虚数成分が負になる素子は電流を進める。

交流理論による解答のヒント
1) 直列回路のインピーダンス(複素数)は、 Z=R+jωL=200+jx2xπx50x1 である。電流は、VをZの絶対値で割ればよく、遅れ角はZの偏角である。
2) 直列回路のインピーダンス(複素数)は、 Z=R-j/(ωC)=50-j/(2xπx103x5x10-6) である。電流は、VをZの絶対値で割ればよく、進み角はZの偏角である。

交流理論を計算するプログラム

#include "stdafx.h"
#include <complex>
using namespace std;

using namespace System;

int main()
{
   Console::Title="やさしい交流理論";

   complex<double> z;
   double R,L,C,F,V,omega;
   String^ string1;

   //第一問
   R=200.0,L=1.0,F=50.0,V=100.0;
   omega=2*Math::PI*F;

   z.real(R);        //インピーダンスの実数部
   z.imag(omega*L);   //インピーダンスの虚数部

   string1=String::Format("1) 電流={0,6:F3}A 偏角={1,6:F3}ラジアン",V/abs(z),arg(z));
   Console::WriteLine(string1);

   //第二問
   R=50.0,C=5.0E-6,F=1.0E+3,V=10.0;
   omega=2*Math::PI*F;

   z.real(R);          //インピーダンスの実数部
   z.imag(-1/(omega*C));  //インピーダンスの虚数部

   string1=String::Format("2) 電流={0,6:F3}A 偏角={1,6:F3}ラジアン",V/abs(z),arg(z));
   Console::WriteLine(string1);

   Console::WriteLine();
   return 0;

}

得られた結果
 図3は得られた結果である。「偏角」は、インピーダンスの偏角をそのまま表示してあるので、電流で見ると、正の偏角は「遅れ」を、負は「進み」を意味する。

図3 得られた結果


「Visual C++ の勉強部屋」(目次)へ