2008. 3.19.
石立 喬

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

――― 連立方程式を解いて、回路計算をする ―――

 簡単な電気回路を例に取り、電気回路の基礎であるキルヒホッフの法則を用いて、多元連立方程式を立て、行列計算を行って結果を得る。次に、同様の回路について、再帰プログラムを用いて計算し、結果を比較する。

キルヒホッフの法則(Kirchhoff’s Law)
 電気回路では、基本中の基本と言われる法則で、ある点(ノード)から枝分かれして広がっている回路があるとき、それぞれの枝に流れ出す電流の合計はゼロであると言う第一法則(電流則、Current Law)と、ループを成している回路があるとき、ループ上の素子に沿って一巡した場合の電圧の合計はゼロであると言う第二法則(電圧則、Voltage Law)とがある。

キルヒホッフの法則を用いたはしご(Ladder)回路の計算
 図1のような、電源Eに対して、はしご状に抵抗が接続されている回路で、キルヒホッフの法則を用いて計算を行い、電圧V〜Vや電流I1〜I5を求める。


図1 キルヒホッフの法則を当てはめる回路


 図1で、ループごとにキルヒホッフの法則を当てはめると、次の式が得られる。

 これをループ電流I〜Iについて整理すると、

となり、5元連立方程式を解けば、各ループ電流が得られる。
 コンピュータ上では、配列の添え字が0から始まるのが普通であるが、電気回路ではそのような習慣がないので、後述のプログラムと対比する場合には注意して欲しい。

キルヒホッフの法則を用いて連立方程式を解くプログラム
 連立方程式の求解は、数値計算では古くからの基本的なテーマの一つで、多くの方法が知られている。なかでも、最もポピュラーなのがガウス・ジョルダンの消去法で、これは、すでに、「Visual C++ 2005 Express Editionの易しい使い方(19)」で紹介している。したがって、ここでは、それをそのまま(float型を一般的なdouble型に変更したが)使用する。
 また、分かりやすくするために、クラスを用いないで、従来のC言語形式で関数を記述してある。関数プロタイプの記述を省略するために、calculateGaussJordan関数をmain関数の前に持ってきてある。
 main関数は、キルヒホッフの法則に当てはめて作成した5元連立方程式の係数を配列aaに設定し、calculateGaussJordan関数を呼び出し、結果を表示する。結果の表示は、電圧V1〜V5、電流I1〜I5、およびV1〜V5の測定点から右の方向を見た抵抗値を示す。各電圧観測点から右のほうを見た抵抗値は、たとえばV1点に対しては、V1/I2により、V2点ではV2/I3によって求めた。点V5の右には抵抗が存在しないので、計算によらず、「∞kΩ」と表示した。

#include "stdafx.h"

using namespace System;

array<double>^ calculateGaussJordan(int n,array<double,2>^ aa)
{
   double p,d,coef_max,temp;
   int i,j,k;
   int pivot;

   for(k=0;k<n;k++){    //係数配列aa[i,j]の列(column)を横方向にスキャン
      //k列で係数の絶対値が最大となる行を探し、pivotに設定
      coef_max=0.0;
      for(i=k;i<n;i++){   //k番目から始めて、行(row)を縦方向にスキャン
         if(Math::Abs(aa[i,k])>coef_max){
            coef_max=Math::Abs(aa[i,k]);
            pivot=i;
         }
      }
      //横方向にjでスキャンして、aa[k,j]とaa[pivot,j]を交換
      for(j=0;j<=n;j++){
         temp=aa[k,j];
         aa[k,j]=aa[pivot,j];
         aa[pivot,j]=temp;
      }
      //aa[k,k]が1になるように、横方向にjでスキャンして、aa[k,k]〜aa[k,n]をaa[k,k]で割る
      p=aa[k,k];
      for(j=k;j<=n;j++)
         aa[k,j]/=p;
      //列を縦方向にiでスキャンして、aa[i,k]がになるようにする
      for(i=0;i<n;i++){
         if(i!=k){
            d=aa[i,k];
            for(j=k;j<=n;j++)
               aa[i,j]-=d*aa[k,j];
         }
      }
   }

   //解を配列bb[0]〜bb[n-1]に入れて返す
   array<double>^ bb=gcnew array<double>(n);
   for(i=0;i<n;i++)
      bb[i]=aa[i,n];

   return bb;

}

int main()
{
   
   Console::Title="電気回路の計算";

   array<double>^ r={1.0,2.0,1.0,2.0,1.0,2.0,1.0,2.0,1.0,2.0};  //抵抗の値の配列
   double e=1.0;                          //電源の値
   array<double>^ i=gcnew array<double>(5);       //ループ電流の値の配列
   array<double>^ v=gcnew array<double>(5);       //ノード電圧の値の配列
   array<double>^ rr=gcnew array<double>(5);      //ノードから右を見た抵抗値の配列
   String^ string1;
   int k;

   //回路の連立方程式
   array<double,2>^ aa={
      {r[0]+r[1],      -r[1],        0,        0,        0,  e},
      {   -r[1],r[1]+r[2]+r[3],      -r[3],        0,        0,  0},
      {     0,      -r[3],r[3]+r[4]+r[5],      -r[5],        0,  0},
      {     0,        0,      -r[5],r[5]+r[6]+r[7],      -r[7],  0},
      {     0,        0,        0,      -r[7],r[7]+r[8]+r[9],  0}
   };

   i=calculateGaussJordan(5,aa);  //連立方程式の求解

   //電圧を求める
   v[0]=e-r[0]*i[0];
   v[1]=v[0]-r[2]*i[1];
   v[2]=v[1]-r[4]*i[2];
   v[3]=v[2]-r[6]*i[3];
   v[4]=v[3]-r[8]*i[4];
   //ノードから右を見た抵抗値を求める
   for(k=0;k<4;k++)
      rr[k]=v[k]/i[k+1];
   for(k=0;k<4;k++){
      string1=String::Format("V{0}={1,5:F3}V  I{2}={3,5:F3}mA  右を見た抵抗値={4,5:F3}kΩ",k+1,v[k],k+1,i[k],rr[k]);
      Console::WriteLine(string1);
   }
      string1=String::Format("V{0}={1,3:F3}V  I{2}={3,5:F3}mA  右を見た抵抗値= ∞kΩ",k+1,v[k],k+1,i[k]);
      Console::WriteLine(string1);

   Console::WriteLine();   //改行
   Console::ReadLine();   //入力待ち

   return 0;
}

キルヒホッフの法則を用いたプログラムの実行結果
 
図2は、プログラムを実行した結果である。


図2 キルヒホッフの法則によって計算した結果

再帰技法を用いたはしご回路の計算
 前述のキルヒホッフの法則を用いたはしご回路の計算は、抵抗R1〜R10を全く任意に独立して選ぶことが可能であった。ただし、プログラムの実例では、1kΩと2kΩの単純な繰り返しを示した。事実、はしご回路は、このような単純なものが多い。図3は、単純なはしご回路の一例で、この場合には、再帰プログラムが有効である。ここでは、この回路を左端の入力側から見た抵抗を求めることにする。


図3 単純なはしご回路の例

 
 図4を別の形で一般化して表現すると、図4のようになる。


図4 はしご回路を再帰技法で解く原理

 
 抵抗の計算は単純な直並列の計算で、図4においてRnを未知の抵抗とすると、


の関係がある。すなわち、Rnを求めるには、Rn-1が分かれば計算できることになり、ここで再帰プログラムが役に立つ。最後に、R0を求める場合には、R-1が存在しない(抵抗が∞)ので、別の式が用いられる。

再帰技法を用いてはしご回路を計算するプログラム
 再帰プログラムは、関数の内部で、再び自分(関数)を呼び出すプログラムである。それでは、無限に実行してしまうので、ストッパーを設ける。nのようなカウンタがその役割を果たし、自分を呼び出す度にnをデクリメントし、nが0になると、自分を呼び出すのを止める。
 再帰用のcalculateResistance関数は、n>0であれば、nを一つ減らしたcalculateResistance(自分)を呼び出して、直並列計算を行い、そうでない(n=0)場合は、単にr1とr2の抵抗の和を返す。
 main関数は、ループの数、すなわちnを変えながら、calculateResistance関数を呼び出し、結果を表示する。

#include "stdafx.h"

using namespace System;

double calculateResistance(int n,double r1,double r2)
{

   if(n>0) return r1+1.0/(1.0/r2+1.0/calculateResistance(n-1,r1,r2));
   else return r1+r2;

}

int main()
{
   
   Console::Title="再帰プログラムによるはしご回路の計算";

   double r,r1=1.0,r2=2.0;
   String^ string1;
   for(int i=0;i<8;i++){
      r=calculateResistance(i,r1,r2);
      string1=String::Format("ループの数が{0} のとき、右を見た抵抗は{1,5:F3}kΩ",i+1,r);
      Console::WriteLine(string1);
   }

   Console::WriteLine();
   Console::ReadLine();

   return 0;

}

再帰技法を用いたプログラムの実行結果
 図5は、上記プログラムを実行した結果である。


図5 再帰プログラムによって計算した結果


 これを、キルヒホッフの法則によって求めた図2と比較すると、良く一致していることが分かる。 すなわち、

       キルヒホッフ法(図2)    再帰法(図5)      抵抗値(両者とも同じ結果)
     -------------------  ----------------    ----------------------
      V1点から右を見たとき   ループ数が4のとき         2.012kΩ
      V2点から右を見たとき   ループ数が3のとき         2.048kΩ
      V3点から右を見たとき   ループ数が2のとき         2.200kΩ
      V4点から右を見たとき   ループ数が1のとき         3.000kΩ

であり、たとえば、V1点から右を見たときにはループが4個あり、どちらの方法でも同じ結果が得られている。

「コマンドプロンプト」ウインドウの設定変更
 「コマンドプロンプト」ウインドウの背景が黒いと、印刷の際に多量の黒インクを消費するので、今後は、背景色を白に、文字色を黒にして表示する。
 このためには、「コマンドプロンプト」ウインドウのタイトルバーを右クリックし、「プロパティ」を選択する。開いたウインドウの「画面の色」タブで、「画面の文字」と「画面の背景」を順次選択し、それぞれカラーを設定して、「OK」をクリックする。「プロパティの適用」ウインドウが開くので、「同じタイトルのウインドウに適用する」にチェックを付けて「OK」をクリックする。


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