3 配列と文字列(教科書の4章)

3.1 配列とは

2節で示した変数6の場合, 一度に確保できるメモリーの領域は1個なので,大量のデータを扱うのは不向きである. 100万個のデータを扱う単純型の変数は不可能である.100万個の変数名 を用意するのはナンセンス.そこで,順序づけられた同じ型のデータが複数ある場合,配 列というデータ構造が考えられた.

これは,同じ型のデータを任意の個数宣言し,配列名と自然数 7でアクセスすることができるようにしたもの である.配列を使うためには,

	int i[10], j[100][100];
のように宣言をする.こうすると, となる.図5のように,メモリー領域が確保される.このデータ構 造では,配列名と添え字(インデックス),たとえばi[3]j[25][49]を指定す ることで,その領域から値を入出力できる.
	i[3]=5;         /* 配列 i[3] に 5 を代入 */
	c=j[25][49];    /* 配列 j[25][49] の値を変数 c へ代入 */
図 5: 配列のイメージ.データを入れる箱がいっぱいある.ただし箱の大きさは全 て同じ.
\includegraphics[keepaspectratio, scale=0.8]{figure/image_array.eps}

添え字が1つのものを一次元配列と言い,それ以上のものを多次元配列と言う.C言語では 多次元配列を使う場合,

	
	int hoge_1[100], hoge_2[100][100], hoge_3[100][100][100];
	double huga[10], huge[10][10], hugo[10][10][10];
のように宣言を行う.これらも,配列名と複数の添え字で,そこにあるデータにアクセス する事ができる.3次元以上ももちろん可能である.

3.2 数列,ベクトル,行列を配列で表現

一次元の配列は数学の数列とベクトルと,二次元の配列は行列とよく似ている.実際,数値計算で 数列やベクトル,行列に関わる数値演算を行うときには,配列が使われる.これ らの数学の表現も,やはり順序づけられた数の集まりにすぎないので,配列と同じである. 一方,スカラー量の場合には,通常の変数として扱えばよい.

数列やベクトル,行列の成分を表す場合,下添え字がつく.その添え字と同じように,配 列の添え字を使う.実に簡単である.ただし,数学の場合,添え字が 1 から始まること が多いが,C言語の場合,それは 0 から始まるので注意が必要である.配列の宣言の時, 添え字部分に書かれるのは要素数であるので,ベクトルや行列の要素数に 1 を加えた数 で領域を確保しなくてはならない.必要数より大きめに確保するのが普通である.

  • 2. 数列やベクトルを配列で表現
  • 3. 行列を配列で表現
  • 表 2: 数列やベクトルを配列で表現
    数学 C言語
    $ a_1$ a[1]
    $ a_2$ a[2]
    $ a_3$ a[3]
    $ \quad\vdots$ $ \quad\vdots$
    $ a_i$ a[i]
    $ a_{i+1}$ a[i+1]
    $ \quad\vdots$ $ \quad\vdots$
    $ a_{2i+1}$ a[2*i+1]
    $ \quad\vdots$ $ \quad\vdots$
    表 3: 行列を配列で表現
    数学 C言語
    $ a_{1\,1}$ a[1][1]
    $ a_{1\,2}$ a[1][2]
    $ a_{1\,3}$ a[1][3]
    $ \quad\vdots$ $ \quad\vdots$
    $ a_{3\,3}$ a[3][3]
    $ a_{3\,4}$ a[3][4]
    $ \quad\vdots$ $ \quad\vdots$
    $ a_{i\,j}$ a[i][j]
    $ \quad\vdots$ $ \quad\vdots$
    $ a_{i+1\,j+1}$ a[i+1][j+1]
    $ \quad\vdots$ $ \quad\vdots$
    $ a_{2i+1\,3j+2}$ a[2*i+1][3*j+2]
    $ \quad\vdots$ $ \quad\vdots$
     

    3.3 配列を使った例

    3.3.1 行列とベクトルの乗算

    3.3.1.1 繰り返し文を使わない例

    配列を使って行列とベクトルのかけ算を行うソースをリスト 2にしめす.この例は要素数が少ない場合であるが,多くな ると,後で学習する繰り返し処理が必要となる.言うまでもないと思うが,行列とベクト ルのかけ算

    \begin{equation*}\begin{aligned}\begin{bmatrix}a_{11} & a_{12} & a_{13} & \ldots...
... \\ c_{2} \\ c_{3} \\ \vdots \\ c_{n} \end{bmatrix} \end{aligned}\end{equation*}

    の演算は,

    $\displaystyle c_i=\sum_{\ell=1}^n a_{i\ell}b_{\ell}$ (2)

    である.

    $ n=2$の場合の計算を行うリスト2の各行の内容は以下の通り である.

    3行
    実数型の配列を宣言.
    5-11行
    配列(行列とベクトル)の要素に値を代入.
    13-14行
    行列とベクトルの乗算.

       1 #include <stdio.h>
       2 
       3 int main(void){
       4   double a[3][3],b[3],c[3];
       5   
       6   a[1][1] = 1.5;                     /* 行列にデータを代入 */
       7   a[1][2] = 2.6;
       8   a[2][1] = -6.3;
       9   a[2][2] = -0.58;
      10 
      11   b[1] = 28.5;                       /* ベクトルにデータを代入 */
      12   b[2] = -19.1;
      13   
      14   c[1] = a[1][1]*b[1]+a[1][2]*b[2];  /* 行列とベクトルの乗算 */
      15   c[2] = a[2][1]*b[1]+a[2][2]*b[2];
      16 
      17   printf("c[1] = %e\n", c[1]);       /* 結果表示 */
      18   printf("c[2] = %e\n", c[2]);
      19   
      20   return 0;
      21 }
    

    3.3.1.2 繰り返し文を使う例

    リスト2の方法だと,次元が大きくなると,プログラムを書くことがで きななくなる.100次元の行列を考えると,不可能であることがわかる.行列の計算のよ うに,同じような計算を繰り返す場合,ループ文をつかう.ループ文を使うと,リスト 2は,リスト3のように書き換えることができる.15 行目〜19行目のループ文の内容は,以下の通りである.
    15行
    forは繰り返しを行え--という命令.繰り返しの回数は,( )内 に記述する.繰り返しの制御には,変数iを使ってる.
    • i=1で,iの初期値を1にしている.
    • i<=2で,iの値が2以下ならば,{ }内を実行する.
    • i++で,iの値を1,増やしている.i=i+1と同じで,iの値を1増加させ たものを,新たなiとする.{ }の内部が実行されるたび,この i++が実行される.
    この最初の繰り返し文で,ベクトル$ c_1, c_2$を計算することを示している.
    16行
    変数jを使って,繰り返しを行っている.ここでは, $ c_i=a_{i1}b_1+c_{i2}b_2$の演 算を行っている.
    17行
    行列の計算.ijの2重ループの内部で,これらの変数が変化しながら 計算を行う.演算子+=は,累算を示している.例えば,a+=ba=a+bとまったく同じでである.これは,右辺のa+bを計算して, その結果を左辺の変数$ a$に代入せよ--と言う意味である.コンピュータ言 語で=は等しいという意味はない.右辺の値を計算して,その結果を 左辺の変数に代入せよという意味である.非常に紛らわしいが,こうなって いる.
    18行
    繰り返し制御変数jを使ったループの終端.
    19行
    繰り返し制御変数iを使ったループの終端.

       1 #include <stdio.h>
       2 
       3 int main(void){
       4   double a[3][3],b[3],c[3];          // 倍精度実数型の配列の宣言
       5   int i,j;                           // 整数型変数の宣言
       6   
       7   a[1][1] = 1.5;                     // 行列にデータを代入
       8   a[1][2] = 2.6;
       9   a[2][1] = -6.3;
      10   a[2][2] = -0.58;
      11 
      12   b[1] = 28.5;                       // ベクトルにデータを代入
      13   b[2] = -19.1;
      14   
      15   for(i=1; i<=2; i++){
      16     for(j=1; j<=2; j++){
      17       c[i] += a[i][j]*b[j];           // 行列とベクトルの乗算
      18     }
      19   }
      20 
      21   printf("c[1] = %e\n", c[1]);       // 結果表示
      22   printf("c[2] = %e\n", c[2]);
      23   
      24   return 0;
      25 }
    
    [練習1]
    リスト2を参考にして, $ \boldsymbol{Mx}$を計算するプログラムを作成せよ.
    [練習2]
    リスト3を参考にして, $ \boldsymbol{Mx}$を計算するプログラムを作成せよ.

    \begin{equation*}\begin{aligned}& \boldsymbol{M}= \begin{bmatrix}1 & 2 & 3 \\ 4 ...
...symbol{x}= \begin{bmatrix}1 \\ 2 \\ 3 \end{bmatrix} \end{aligned}\end{equation*}

    3.3.2 フィボナッチ数列

    次のサンプルプログラムは,フィボナッチ(Fibonatti)数列の問題である.
    フィボナッチのウサギ 成熟した1つがいのウサギは,1ヶ月後に1つがいのウサギを生むとする.そして,生まれ たウサギは1ヶ月かけて成熟して次の月から毎月1つがいのウサギを生む.全てのウサギ はこの規則に従うとし,死ぬことは無いとする.1つがいのウサギは,1年後には何つが いになるか.2,3年後はどうなっているだろうか?.計算してみると分かるが,恐ろしい ことになっている.

    この数列は単純で,

    \begin{equation*}\left\{ \begin{aligned}&F_0=1 \\ &F_1=2 \\ &F_{k}=F_{k-1}+F_{k-2} \end{aligned} \right.\end{equation*}

    となっている.この単純な数列が,自然界のいろいろな場所でお目にかかれるらしい.か なり不思議なことのようなので,興味のあるものは調べてみると良い.

    フィボナッチ数列$ F_k$を計算するソースをリスト4にしめす. 各行の内容は以下の通りである.

    9-11行
    for文(教科書p.142)は繰り返しに使われる.ここでは,変数 tuki の値を2〜36まで,一つずつ増加させている.中括弧{ } 内の文を1 回実行させるたびに,変数tukiの値を増やしている.

       1 #include <stdio.h>
       2 
       3 int main(void){
       4   int usagi[100];         // 月ごとの兎の数
       5   int tuki;               // 月を表す.ループ制御変数
       6   
       7   usagi[0]=1;
       8   usagi[1]=2;
       9 
      10   for(tuki=2; tuki<37; tuki++){
      11     usagi[tuki] = usagi[tuki-1] + usagi[tuki-2];
      12   }
      13 
      14   printf("after 1 year  : %d\n", usagi[12]);
      15   printf("after 2 years : %d\n", usagi[24]);
      16   printf("after 3 years : %d\n", usagi[36]);
      17   
      18   return 0;
      19 }
    
    [練習1]
    消費者金融の利子を見ると恐ろしいものがある.CMなどをみ ると年間20%くらいである.100万円借りた場合,次の単利と複 利の場合の10年後の利息を計算せよ.
    • 単利の場合,元金の100万円のみに利子が付く.
    • 複利の場合,元金と利息にも利子が付く.
    消費者金融の利子は複利である.可愛いお姉さんが宣伝して いるが,かなりの利子がつくことが分かるであろう.
    [練習2]
    時間が余った者のみ,チャレンジせよ.この問題は参考文献  [2]から引用した.
    Bernadelliはある種類のカブトムシについて考察した.その カブトムシは,3年間で成長し,3年目につぎの世代を生んで 死亡する.3年間のうち第一年目で確率 1/2 で生き残り,さ らに第2年目で 1/3 が生き残り,第3年目でそれぞれの雌が6 匹の雌を生む.これに対応する行列は,

    $\displaystyle \begin{bmatrix}b_{k\,1} \\ b_{k\,2} \\ b_{k\,3} \end{bmatrix}= \b...
...nd{bmatrix} \begin{bmatrix}b_{k-1\,1} \\ b_{k-1\,2} \\ b_{k-1\,3} \end{bmatrix}$    

    とかける.ここで,$ b_{k\,1}$$ k$年のときの1年目のカブ トムシの数である.

    1年目,2年目,3年目の虫がそれぞれ3000匹いた としたときその年以後6年間の虫の分布を求めよ.

    3.4 文字列

    これは,数値計算ではあまり使わないので,興味のあるものは教科書を読んでおくように.
    ホームページ: Yamamoto's laboratory
    著者: 山本昌志
    Yamamoto Masashi
    平成18年5月2日


    no counter