11.C言語プログラミング入門(変数の概念,for文,if文,数学関数,配列など)

 Cは,UNIXのシステム記述言語として使われており,初心者にとってはPascalに比べてやや難しいと感じる言語である.平均値と標準偏差を求めるプログラムと,行列の積を求めるプログラムを紹介しながら,基本的な事項について説明する.

 

11.1 平均値と標準偏差

 ある集団の数値データを読みこませて,平均値・分散・標準偏差を計算するプログラムを作成しよう.平均値m (mean value),分散v (variance),標準偏差s (standard deviation)は,数値データがdi( i=1n )に格納されていれば,データの個数をn個として次の式で求めることができる.

 

m = (Σdi) / n , v = { Σ( di - m )2 } / n , s = v

 

プログラムの中で,数値データはファイル名 input.dat として事前に作成しておき,配列dの中に読み込ませる.作成した input.dat の中をUNIX上で確認すると以下のように33個のデータが表示された.

 

[bridge:/export/home/satoh]11 :cat input.dat

160 161 155 158 157 163 163 168 153 160

168 151 152 160 155 164 161 166 156 164

157 160 155 155 167 162 159 160 158 158

165 157 160

 

 数値データの形は,3桁の整数であるので配列dは整数型にするが,それ以外の変数は実数型にする.この理由は,計算途中で除算があるためである.Cでは,fopen を使ってファイルをオープンするが,オープンしたいファイル input.dat が無かった場合に備えて NULLを返すようにしておき,exit でプログラムを強制終了させる.次に,配列 d の中へデータを読み込ませるためにfscanf を使う.データは,ファイルポインタ fp が示す input.dat から %d (整数型10進数:decimal)に従って入力される.データを読み終わったらfclose を用いてファイルを閉じる.fclose は,コンピュータに不用なファイルを教えるために必要であり,Cでは input.dat ファイルのために一時的に確保されたメモリが解放される.

 作成したプログラム例をリスト11.1に示す.

 

リスト11.1  平均値・分散・標準偏差を求めるプログラム mean.c

#include <stdio.h>

#include <math.h>

#include <stdlib.h>

 

void main( void )

{

int d[ 33 ];

double m, v, s;

int n = 33;

int i;

double sum;

FILE *fp;

 

if(( fp = fopen( "input.dat", "r" )) == NULL )

{

perror( "Can not open file. \n");

exit(1);

}

 

for ( i = 0; i < n; i++ )

{

fscanf( fp, "%d", &d[ i ] );

}

 

fclose( fp );

 

printf("----- INPUT DATA -----\n");

for ( i = 0; i < n; i++ )

{

printf( "%d ", d[i] );

}

 

for ( sum = 0.0, i = 0; i < n; i++ )

{

sum += d[ i ];

}

 

m = sum / n;

 

for( sum = 0.0, i = 0; i < n; i++ )

{

sum += ( d[ i ] - m )*( d[ i ] - m );

}

v = sum / n;

s = sqrt( v );

 

printf( "\n" );

printf( "mean value =%10.3f\n", m );

printf( "variance =%10.3f\n", v );

printf( "standard deviation =%10.3f\n", s );

}

 

 このプログラムをUNIX 上でコンパイルする場合,実行ファイル名をプログラム名と同じmean としておく.実行ファイル名とプログラム名を関連づけるためである.また,数学関数 sqrt を使用しているため -lm を追加する.コンパイル時にエラーが生じなければ,実行ファイル meanが出来上がるので,実行ファイル名を入力する.以下に,コンパイルと実行結果を示す.

 

[bridge:/export/home/satoh]12 :cc -o mean mean.c -lm

[bridge:/export/home/satoh]13 :mean

----- INPUT DATA -----

160 161 155 158 157 163 163 168 153 160 168 151 152 160 155

164 161 166 156 164 157 160 155 155 167 162 159 160 158 158

165 157 160

mean value = 159.636

variance = 19.383

standard deviation = 4.403

 

 このプログラムの課題は,データ数が33個に限定される点である.なお,なんらかの原因で入力データ input.dat が存在しないと,perror関数がエラーメッセージを以下のように

Can not open file.

:No such file or directory

と表示するので,fopen でファイルをオープンする際に perror を使用すると良い.

 

11.2 行列の積

行列A( 3 行 4 列 )と行列B( 4行 5列 )の積を計算して,行列C( 3 行 5 列 )を求めるプログラムを作成しよう.行列Aと行列Bは,ファイル名 matrix.dat として事前に作成しておく.作成した matrix.dat をUNIX上で確認すると以下のように表示された.

 

[bridge:/export/home/satoh]14 :cat matrix.dat 0.1 0.2 0.3 0.4

1.1 1.2 1.3 1.4

2.1 2.2 2.3 2.4

3.1 3.2 3.3 3.4 3.5

4.1 4.2 4.3 4.4 4.5

5.1 5.2 5.3 5.4 5.5

6.1 6.2 6.3 6.4 6.5

 

 データの形は実数であるので各行列を表現するための二次元配列は実数型とする.事前に作成したファイル matrix.dat をオープンして,二次元配列 a[ N ][ L ]と b[ L ][ M ]の中へデータを読み込ませるために,fopen とfscanf そして fclose を使用する.fscanf で記述している %f は,float型(浮動小数点型)と double型(倍精度浮動少数点型)の両方のデータに対応している.

 for 文が二重になっているが,i とj の初期値は0(ゼロ)である.まず内側の j の値が増加して0 に戻ったときにi が 1 に変わる点に注意されたい.matirix.dat 内の各値が正しく読み込まれているかを確認するために printf を使って行列Aと行列Bをディスプレイに表示させる.

 作成したプログラム例をリスト11.2 に示す.

 

リスト11.2  行列の積を求めるプログラム matrix.c

#include <stdio.h>

#include <stdlib.h>

#define N 3

#define L 4

#define M 5

void main( void )

{

float a[ N ][ L ];

float b[ L ][ M ];

float c[ N ][ M ];

int i, j, k;

FILE *fp;

 

if(( fp = fopen( "matrix.dat", "r" )) == NULL )

{

perror( "Can not open file. \n" );

exit(1);

}

 

for ( i = 0; i < N; i++ )

{

for ( j = 0; j < L; j++ )

{

fscanf( fp, "%f", &a[i][j] );

}

}

 

for ( i = 0; i < L; i++ )

{

for ( j = 0; j < M; j++ )

{

fscanf( fp, "%f", &b[i][j] );

}

}

 

fclose( fp );

 

printf(" ----- MATRIX A -----\n");

for ( i = 0; i < N; i++ )

{

for ( j = 0; j < L; j++ )

printf( "%10.5f ", a[i][j] );

printf( "\n" );

}

 

printf(" ----- MATRIX B -----\n");

for ( i = 0; i < L; i++ )

{

for ( j = 0; j < M; j++ )

printf( "%10.5f ", b[i][j] );

printf( "\n" );

}

 

for ( i = 0; i < N; i++ )

{

for ( j = 0; j < M; j++)

{

c[i][j] = 0;

for ( k = 0; k < L; k++ )

{

c[i][j] += a[i][k] * b[k][j];

}

}

}

 

printf( "\n" );

printf( " ----- MATRIX C -----\n" );

for ( i = 0; i < N; i++)

{

for ( j = 0; j < M; j++ )

{

printf( "%10.5f ", c[i][j] );

}

printf( "\n" );

}

}

 このプログラムをUNIX上でコンパイルして実行ファイル matrix をつくる.以下に,コンパイルと実行結果を示す.なお,数学関数は使用していないためオプションの ?lm は不用である.

 

[bridge:/export/home/satoh]15 :cc -o matrix matrix.c

[bridge:/export/home/satoh]16 :matrix

----- MATRIX A -----

0.10000 0.20000 0.30000 0.40000

1.10000 1.20000 1.30000 1.40000

2.10000 2.20000 2.30000 2.40000

----- MATRIX B -----

3.10000 3.20000 3.30000 3.40000 3.50000

4.10000 4.20000 4.30000 4.40000 4.50000

5.10000 5.20000 5.30000 5.40000 5.50000

6.10000 6.20000 6.30000 6.40000 6.50000

----- MATRIX C -----

5.10000 5.20000 5.30000 5.40000 5.50000

23.50000 24.00000 24.50000 25.00000 25.50000

41.90000 42.80000 43.70000 44.60000 45.50000

 

 配列を用いて数学的な計算をする場合,配列の最初の要素番号が0(ゼロ)から始まっていることに気をつけたい.数学的な意味では添字が1から始まっているために,

 

for ( k = 0; k < L; k++ ) とすべきところを, for ( k = 1; k <= L; k++ )

 

としやすい.配列内の各要素の指定場所がずれて正しい結果が得られないので十分に注意しよう.

 2つのプログラム例に共通している点は,読み込ませる数値データをファイルとして扱っている点と,for文で使用するカウンタ変数 ( i,j,k など)を整数型としている点である.浮動小数点型をカウンタ変数に使わない理由は,少数点以下の演算に誤差が生じるためである.
また,for 文の 中で < (意味:小なり)を用いている点も大変重要である.仮に,k 値がL値を飛び越えた場合にも対応しており暴走することはない.

 

11.3 Pascal言語によるプログラム例

 Pascalは,教育用言語として広く使用されている.ここでは,平均値と標準偏差のプログラム例を示す.11.1 のC言語によるプログラム例と比較されたい.

 

リスト11.3 平均値・分散・標準偏差を求めるプログラム mean.pas

program mean(input, output);

var d : array[1..33] of integer;

m, v, s : real;

n, i : integer;

sum : real;

begin n := 33;

for i := 1 to n do

readln( d[ i ] );

sum := 0;

for i := 1 to n do

sum := sum + d[ i ];

m := sum / n;

sum := 0;

for i := 1 to n do

sum := sum + sqr( d[ i ] - m );

v := sum / n;

s := sqrt( v );

writeln('mean value =',m:10:3);

writeln('variance =',v:10:3);

writeln('standard deviation =',s:10:3);

end.

 

 このプログラムでは,33個の入力データをキーボードから直接入力する.入力の際は,一つ一つのデータごとにエンター・キー(リターン・キー)を押して33個のデータを配列 d[ i ] の中へ読み込ませる.なお,プログラム中の sqr は,C言語では使用しない関数であり,平方を意味する.以下に実行結果を示す.

mean value = 159.636

variance = 19.383

standard deviation = 4.403

 

また,11.1のC言語によるプログラム例では,sum の値を求める際に,加算する目的の代入演算子 += を使用しているが,Pascalでは代入演算子 += を使用できない点に注意されたい.

(ここに追加の3つの表を入れる)

 

参考文献

内田 智史 編:C言語によるプログラミング[基礎編],Ohmsha,1998.

間野 浩太郎 監修・内田 智史 著:TURBO Pascalプログラミング,Ohmsha,1990.