情報基礎A 「Cプログラミング」(ステップ7)
このステップの目標
- 配列変数を用い、配列要素への適正なアクセスに配慮したプログラムが書ける
- やや複雑な問題でも、段階的に、データ表現とそれに対応したアルゴリズムの選択ができる
1.配列を使った処理
コンピュータを使って大量のデータを処理するために必須の機能のひとつ、それが配列だ。 「プログラミングの入門編」といえども、この配列はマスターしておきたい。
配列変数:沢山のメモリー(変数)を一括して確保し、通し番号でアクセスする
これまで書いてきたプログラムでは、変数(メモリー)が必要になったら、いちいちそれに名前を付けて、
int are ; float kore ;
のように宣言していた。ところが、例えば、1000人から成る集団があって、それぞれの情報(例えば体重)を使った処理をしたいような場合、 同じような変数宣言を1000回も書くのはごめんこうむりたい。 このように、データの件数が多い場合(あるいは、データの件数が不定の場合)には、必要な個数のメモリを一括して宣言し、それぞれを通し番号(**番目)で管理するのが理にかなっている。
C言語に限らず、一般のプログラミング言語では、こうした用途に使うために、配列型(array type)のデータ構造が用意されている。

例えば、int型の変数を100個、float型の変数を1000個、一括して準備したい場合は、
int are[100] ; float kore[1000] ;
のように宣言する。
配列変数へのアクセス
これらの変数の中身(データ)には、通し番号を付けて要素ごとにアクセスする。例えば、
are[3] = 17 ; kore[ i ] = kore [ i-1 ] + kore [ i+1 ] ;
のように。つまり [ ] の中に、何番目のハコであるかを指定するわけだ。ハコの場所は、整数値で指定しても良いし、変数や数式であっても構わない。
配列へのアクセスの「はみ出し」は、コンパイル時には検出できないので、とても厄介だ。
ここで、気をつけなければならないのは、通し番号は0から始まることだ。
100個の要素から成る配列を宣言したら、おしまいの要素の番号は99である(図参照)
あらかじめ宣言した範囲を超えた要素(例えば kore[-1]
やkore[100]
)にアクセスすると、
プログラムは「予期せぬ動作」をするので、十分注意すること。
配列データのコピー
配列変数の中身をそっくりコピーした場合、
int a[100],b[100] ; ・・・ a = b ;
のように書いてはいけない。「配列へのアクセスは要素ごとに行う」のがルールである。 であるから、面倒だけれども、
int a[100],b[100] ; int i ; ・・・ for (i=0; i<100; i=i+1) a[i] = b[i] ;
のように、反復処理を使って、要素毎に逐一代入する必要がある。
配列を使ったプログラムの例
所謂数値計算や、統計処理などを行う際に、あらかじめ配列にデータをセットしておいてから、あれこれ計算を進めるのが効率的である。 以下は、配列中のデータから一番大きな値を探し出すプログラムの例。
例題8(ex8.c)
配列中のデータの中から最大値を探し出す。
これくらいのデータ件数では、プログラムで処理する有難味が薄いけれども、仮にデータが1000件ともなれば、
最大値を見つけるだけでも人手で行うのは大変だろう。
#include <stdio.h> main( ) { float x[10] = { 2.1, 5.3, 7.3, 4.7, 9.2, 1.2, 3.1, 5.3, 8.3, 4.8 } ; int i ; float maxval ; maxval = -100000 ; for (i=0 ; i<10 ; i=i+1) { if (x[i] > maxval) { maxval = x[i] ; } } printf ("最大値= %f\n",maxval) ; }
プログラムの見所
配列変数の初期値を設定する場合に、いちいち、
x[0] = 2.1 ; x[1] = 5.3 ;
配列変数の初期化
のように書くのは手間がかかるので、上記の例のように、まとめて
のような書き方が許されている。ただし、この 配列={ };
の記法は、(プログラムの始まりの部分の)変数宣言でのみ使える。
配列データの中から最大値を探す手順
8行目から16行目にかけてが、配列の中から最大値を見つける処理。 基本的な考え方は、すでにステップ6で説明してあるので、そちらを参照のこと。 念のために、手順を言葉に「翻訳」すると以下のとおり:
仮の最大値を、非常に小さい数値に設定しておく 配列の中身を0番目から最後まで順に調べる: もし、i番目の値が、仮の最大値より大きければ、仮の最大値としてi番目の値をセットする この時点での、仮の最大値が、配列全体を通しての最大値
上の手順の一部をちょっとだけ変更すれば、最小値を見つけることもできる。
次に、もう少し複雑な処理の例として、 配列中の数値の並び順を、小さい方から大きい方へ順に(昇順に)並べ変え、出力するプログラムを示す:
例題9(ex9.c)
配列データの整列(ソーティング)
#include <stdio.h> main( ) { float x[10] = { 2.1, 5.3, 7.3, 4.7, 9.2, 1.2, 3.1, 5.3, 8.3, 4.8 } ; int i,j,n ; float w,median ; for (i=0 ; i<9 ; i=i+1) { for (j=i+1 ; j<10 ; j=j+1) { if (x[i]>x[j]) { w = x[i] ; x[i] = x[j] ; x[j] = w ; } } } for (i=0 ; i<9 ; i=i+1) { printf ("%f \n",x[i]) ; } }
プログラムの見所
ソーティングについての 補足説明はこちら
プログラム8〜16行目にかけてが、配列データの並べ変え(整列:ソーティング)を行っている部分。 ソーティングの仕組みについては、機会を改めて学ぶことにして、 ここでは、(i)変数(iとj)を使った配列変数の添え字のコントロール、 (ii)配列変数への(からの)変数の代入・配列変数を含む条件式の書き方、のふたつを、 「ふむふむ」と眺めるだけに留めたい。
2.配列を使った統計計算
前のステップで行った統計計算プログラムを、配列を使ったバージョンに変更してみよう。 先にまず、全データを配列変数に格納して、その後で、平均の計算を行っている。 配列変数が登場する以外には、特に目新しい箇所は無いはずだ。
例題6A(ex6a.c)
#include <stdio.h> #include <math.h> main( ) { int n,i ; float x[100] ; float sum,average ; printf("データの件数を入れてください:") ; scanf("%d",&n) ; for (i=0 ; i<n ; i=i+1) { printf("%d 番目のデータ:", i+1) ; scanf("%f",&x[i]) ; } sum = 0 ; for (i=0 ; i<n ; i=i+1) { sum = sum + x[i] ; } printf("データの総和は %f です\n",sum) ; average = sum / n ; printf("平均値は %f です\n",average) ; }
例題6Aの見所
- 配列の大きさは、データを入れるのに十分な数を用意しておく(例題では100)。
- 配列の要素は、0番目から始まる。それに対応して、ループ変数のiを0からn-1まで動かしている。
- 何番目のデータかを表示する際、「0番目」という表現は多少違和感があるので、i+1の値をプリントするようにしてある。
練習:標準偏差の計算
例題6Aを発展させて,平均値だけでなく標準偏差も計算して,(両方を)表示するプログラムを書いてみなさい。
ヒント
平均は,上の例のように,データをすべて合計して,それを件数で割ればよい。
標準偏差の計算手順
標準偏差は,「平均値からの差を二乗した値の合計を求め,それを件数で割った後で,平方根を取った値」 である。 けれども,その値は「二乗の平均値から 平均の二乗を引いた値の平方根」に等しいことを簡単に示せる。 具体的には,以下の手順で計算すればよい:
- $x_i \; (i=1,2, \cdots , N)$を$i$番目のデータとする。データの件数は$N$とする。
- データの総和$S$を計算する:\[ S= \sum_{i=1}^N x_i \]
- 二乗の総和$U$を計算する \[U = \sum_{i=1}^N \left( x_i \right)^2\]
- 平均 $E=\frac{S}{N}$ を求める
- 二乗の平均を $V = \frac{U}{N}$ によって求める
- そうすると標準偏差は $\sigma = \sqrt{V - E^2}$
2と3はforループを使って計算し(言うまでもなく、ステップ4で学んだ「総和のパターン」を使う。 2と3をひとつのforループの中でまとめて計算してしまうのがスマート)、 それらの総和の結果を使って$E$と$V$を求める。そして公式に従って標準偏差を算出する。
配列を使うと、添え字を伴う数式、例えばベクトルや行列などの計算を、自然な形で記述することができて、とても便利だ。
この例では、数式の$x_i$と、配列のx[i]
を対応させながらプログラムが書ける。
ここで、添字の対応関係(具体的には、配列の添え字の範囲は0から始まり(要素数)-1までであること)には、くれぐれも注意すること。
平方根の計算には、これまでにも何度か登場した sqrt( )
を用いる。
その際に、プログラム冒頭の#include <math.h>
と、コンパイル時の -lm
オプションを忘れぬように。