計算数学演習第7回

配列

以下の内容で演習を行います。

  • 配列変数
  • 配列と繰り返し文を用いた演算

今日の目標

配列を理解する。
繰り返し文や配列変数のメリットを利用する。

配列変数の宣言と利用

C言語に限らず、ほとんどのプログラミング言語には配列変数と呼ばれるものがあります。
配列変数は例えば次のように宣言することで用意することができます。
int a[5];
これにより、次のように整数型の変数が5個用意されることになります。
a[0], a[1], a[2], a[3], a[4]
かぎ括弧 [ ] で囲まれた数字は添字と呼ばれます。 このように添字は 0 から始まるので注意が必要です。

次のサンプルプログラムを array-1.c として打ち込んで実行してみてください。
このプログラムは5個の整数を入力すると、入力値を逆の順番で表示するプログラムです。

array-1.c

#include <stdio.h>
 
int main() {
    /* 整数型変数の宣言 */
    int i, n, c[5];
 
    /* c[0] ~ c[4] の入力 */
    for (i = 0; i < 5; i++) {
        printf("整数を入力してください:c[%d]= ", i);
        scanf("%d", &c[i]);
    }
 
    /* c[4] ~ c[0] の出力 */
    for (i = 0; i < 5; i++) {
        printf("c[%d]=%d\n", 4-i,c[4-i]);
    }
 
    return 0;
}

整数型の配列以外にも実数型の配列も作ることができます。
ただし、配列変数の添字には整数型の変数を使います。
double u[6];
と宣言することにより、
u[0], u[1], u[2], u[3], u[4], u[5]
と、5つの実数型変数を作ることができます。

ベクトルの計算(内積の計算)

例えば配列変数 u[i] をベクトル u の i 成分とみなすと、いくつかベクトルの計算が簡単にできることが分かります。以下は二つの3次元ベクトル u, v の内積を求めるプログラム (array-2.c) です。

array-2.c

#include <stdio.h>
 
int main() {
    /* 変数の宣言 */
    int i;
 
    /*(ベクトルの各成分は配列で宣言)*/
    double u[3], v[3], naiseki;
 
    /* ベクトルの各成分 u[0] ~ u[2] の入力 */
    for (i = 0; i < 3; i++) {
        printf("ベクトル u の第%d成分を入力してください: ", i);
        scanf("%lf", &u[i]);
    }
 
    /* ベクトルの各成分 v[0] ~ v[2] の入力 */
    for (i = 0; i < 3; i++) {
        printf("ベクトル v の第%d成分を入力してください: ", i);
        scanf("%lf", &v[i]);
    }
 
    /* 内積の初期化 */
    naiseki = 0.0;
 
    /* 内積の計算 */
    for (i = 0; i < 3; i++) {
        naiseki = naiseki + u[i]*v[i];
    }
 
    printf("内積=%lf\n", naiseki);
 
    return 0;
}

課題1

array-2.c を、2つの5次元ベクトルの内積を求めるプログラムに改造せよ。

課題1’

array-2.c を以下のように改造せよ。

  • ベクトルの次元数nを入力
  • 入力されたnを用いて、2つのn次元ベクトルの内積を求める

複数の入力値の最大値(最小値)を求める

例えば、実数10個を入力してその最大値を求めるには以下のようにします (array-3.c) 。最大値を求めるときには、一つ変数を別に準備してそれと比べるようにすると簡単です。

array-3.c

#include <stdio.h>
 
int main() {
 
    /* 整数型変数の宣言 */
    int i;
    double max, c[10];
 
    /* 実数型配列に値を入力する繰り返し文 */
    for (i = 0; i < 10; i++) {
        printf("実数を10個入力してください。(%d つ目) : ", i+1);
        scanf("%lf", &c[i]);
    }
 
    /* max の初期化 */
    max = c[0];
 
    /* 大小を比べる繰り返し文 */
    for (i = 1; i < 10; i++) {
        if (max < c[i]) {
            max = c[i];
        }
    }
 
    /* 結果を表示 */
    printf(" max = %lf \n", max);
 
    return 0;
}

課題2

array-3.c を、最大値と最小値と平均値を表示するように改造せよ。

課題3

10 個の実数 R[0] ~R[9] を入力すると、その内の最大値と2番目に大きい値を表示するプログラムを作成せよ。
(ヒント:まず最も大きな値 max を求め、そのあとで max > R[i] となる最も大きい R[i] を求めて表示.)

多次元配列変数

多次元配列というものもあります。 多次元配列を用意するには要素を次元数だけ並べて宣言するだけです。
例えば、
int a[2][3];
などのように書くと整数型の a[0][0], a[0][1], a[0][2], a[1][0], a[1][1], a[1][2] の6個が用意されます。
1次元配列と同様に、 double f[2][3]; とすれば実数型の配列が宣言され、 いずれの場合も配列変数の添字には整数型の変数が使えます。
これを使うと行列のかけ算などが簡単にできます。 以下のプログラムは

    \[A = \left( \begin{array}{lll} 3 & 2 & 5 \\ 4 & 1 & 9 \end{array} \right), B = \left(\begin{array}{l} 7 \\ 8 \\ 2\end{array}\right), C=AB\]

の行列Cの要素を計算するプログラムです (array-4.c) 。打ち込んで実行してください。

array-4.c

#include <stdio.h>
 
int main() {
    /* 整数型変数の宣言 */
    int i, j, a[2][3], b[3], c[2];
 
    /* 整数型配列 a に値を入力 */
    a[0][0] = 3;
    a[0][1] = 2;
    a[0][2] = 5; 
    a[1][0] = 4;
    a[1][1] = 1;
    a[1][2] = 9;
 
    /* 整数型配列 b に値を入力 */
    b[0] = 7;
    b[1] = 8;
    b[2] = 2;
 
    /* 整数型配列 c の初期化 */
    for (i = 0; i < 2; i++) {
        c[i] = 0;
    }
 
    /* 各成分を計算する繰り返し文 */
    for (i = 0; i < 2; i++) {
        for (j = 0; j < 3; j++) {
            c[i] = c[i] + a[i][j] * b[j];
        }
    }
 
    /* 結果を表示する繰り返し文 */
    for (i = 0; i < 2; i++) {
        printf(" %d行目の成分 = %d \n", i+1, c[i]);
    }
 
    return 0;
}

上の例のように、ベクトルや行列などのすでに値が決まっているものに対して、それぞれ値を代入するのが面倒、と感じるかもしれません。次のように変数の宣言にあわせて定義することもできます。

array-4b.c

#include <stdio.h>
 
int main() {
    /* 整数型変数の宣言 */
    int i, j;
    int a[2][3] = {{3,2,5},{4,1,9}};
    int b[3] = {7,8,2};
    int c[2];
 
    /* 整数型配列 c の初期化 */
    for (i = 0; i < 2; i++) {
        c[i] = 0;
    }
 
    /* 各成分を計算する繰り返し文 */
    for (i = 0; i < 2; i++) {
        for (j = 0; j < 3; j++) {
            c[i] = c[i] + a[i][j] * b[j];
        }
    }
    
    /* 結果を表示する繰り返し文 */
    for (i = 0; i < 2; i++) {
        printf(" %d行目の成分 = %d \n", i+1, c[i]);
    }
 
    return 0;
}

課題4

    \[A = \left( \begin{array}{ll} 1 & -2 \\ 1 & 4 \end{array} \right), r_1= \left(\begin{array}{l} 1 \\ -1 \end{array}\right), r_2= \left(\begin{array}{l} 2 \\ -1 \end{array}\right)\]

とする。
このとき、 A^3 r_1, A^3r_2 を求めるプログラムを作成せよ。
(実は、r1, r2は行列Aの固有ベクトルで、それぞれ固有値3, 2に対応する)
次の課題5でこのプログラムを再利用することを考えて、行列の三乗を求めるのではなく、

    \[A(A(Ar_1))\]

を求めることをお勧めする。

課題5

非負の整数nについて、以下の関係式が成り立つとする。

    \[v_{n+1} = \frac{Av_n}{||Av_n||}\]

ただし、

    \[A = \left( \begin{array}{ll} 1 & -2 \\ 1 & 4 \end{array} \right), v_0 = \left( \begin{array}{l} x_0 \\ y_0 \end{array} \right)\]

であるとし、||・||はベクトルの長さ(自身との内積の平方根)を表すものとする。
実数x_0, y_0の入力を促し、v_{100}の第1成分と第2成分を表示するプログラムを作成せよ。
様々なx_0, y_0を入れて、実行してみてください。
結果を固有ベクトルr1、r2と見比べて、
なぜ、そのような結果になるのか考えてみてください。

課題6

(array-4.c 等を参考に) 5 行 5 列 (n 行 n 列) の行列同士のかけ算のプログラムを作成せよ。

(おまけ)数値の入力回数をカウントする

次のプログラムは、 1 ~ 9 いずれかの自然数を打ち込むと、それぞれ何回入力されたかを数えるプログラムです (array-5.c) 。0 を入力することで入力を終えることができるようになっています。
こういった構造は、分布やヒストグラムを作成する際に有用です。(途中 if 文の入れ子があります。入れ子構造を分かりやすくするために字下げが行われていることに注意してください。字下げによって、構造がよくわかるはずです。)
– – – – – – – – – – – – – – – –

array-5.c

#include <stdio.h>
 
int main() {
 
    /* 整数型変数の宣言 */
    int i, n, c[9];
 
    /* 整数型配列の初期化を行う繰り返し文 */
    for (i = 0; i < 9; i++) {
        c[i] = 0;
    }
 
    /* n の初期化(while文の条件式をはじめから満たさないようにするため) */
    n = -1;
 
    /* 0 が入力されるまで入力を受けつづける繰り返し文 */
    while (n != 0) {
        printf("1 ~ 9 いずれかを入力してください(0 で入力終わり): ");
        scanf("%d", &n);
 
        /* 入力された n の妥当性をチェック */
        if ((n <=0) || (n > 9)) {
            if(n == 0) {
                /* n が 0 だった場合 */
                printf("0 が入力されました。入力を終わります。\n");
            }
 
            else {
                /* n の範囲が不当だった場合 */
                printf("1 ~ 9 以外の数値が入力されました。無視します。\n");
            }
        }
 
        else {
            /* n が 1 ~ 9 であった場合、c[n-1] の値を一つ増やす */
            /* n-1 となっているのは、添え字が 0 から始まることによる */
            c[n-1]=c[n-1]+1;
        }
    }
 
    /* 結果を表示する繰り返し文 */
    for (i = 0; i < 9; i++) {
        printf("%d が %d 回入力されました。\n", i+1, c[i]);
    }
 
    return 0;
}

課題7

10個の実数 A[0] ~ A[9] を入力すると、それらを値の大きい順に並べ替えて表示するプログラムを作成せよ。
※ ソート問題と呼ばれるものです。ソート問題に対するアルゴリズムは色々ありますが、まずは計算速度度外視で、隣接する数値を入れ替えていくバブルソートが良いでしょう。余裕があるなら、選択ソートやクイックソートなどを実装してみましょう。

Department of Mathematical and Life Sciences, Graduate School of Integrated Sciences for Life, Hiroshima University