私は趣味でプログラミングをしているので、たまーにteratailやYahoo知恵袋でプログラミング関係の質問を閲覧していますが、毎年大学の課題と思しき「C言語で最大、最小、平均、標準偏差を出力するプログラムを教えてください!」的な質問が出てきます。
パッとGoogleで調べてみても同じような質問だったり解説サイトがすぐに出てくるのですが、多分ピンポイントに答えだけが欲しいんでしょう。
そんな方々に向けて、もう不毛な(というかリソースの無駄な)質問はやめてもらうために、ピンポイントに答えだけ(一応解説付き)を用意しましょう!
解説が不要な方
解説が不要な方もおられるかと思いますので、先に答えだけ載せておきます。
目に入ってしまうのも嫌な方もいるかと思うので、IdeoneのURLを記載しておくのでそちらからご自由にソースをお持ちになってください。
実行すると、乱数で生成された10個のデータとそれぞれの結果が出力されるようになります。
1 2 3 4 5 |
81 32 61 96 69 80 72 81 67 54 最大 :96 最小 :32 平均 :69.300000 標準偏差:16.757386 |
手段とソースの解説
さて、それでは各問題に対して解説を行なっていきます。
本ページではデータは乱数で生成しているので、ついでにその方法についても簡単に解説しておきます。
乱数でデータ生成
C言語で乱数を扱うためにはrand関数を使用しますが、乱数の初期化をしないと毎回同じ値となってしまうため、srand関数を使用して乱数の初期化も行います。
1 2 3 4 5 6 7 8 9 10 |
#include <stdlib.h> // rand, srand関数を使用するために必要 #include <time.h> // time関数を使用するために必要 void hoge() { // シードの初期化 srand((unsigned)time(NULL)); // 乱数の取得(以下の場合だと0〜99の間の乱数) int r = rand() % 100; } |
乱数を初期化するためのシード(種)には、一般的にはtime関数を使用します。
プログラム実行中に一度だけsrand関数が実行されれば良いので、あとは必要なデータ数分rand関数によって乱数を取得すればデータの生成は完了です。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 |
#include <stdlib.h> #include <time.h> // データ数 #define N 10 // データ生成関数 void createData(int data[N]) { // ループ変数 int idx = 0; // 乱数のシード初期化 srand((unsigned)time(NULL)); // ループでN個分乱数データを作成 for(idx = 0; idx < N; idx++) { // データに乱数値を設定 data[idx] = rand() % 100; } } |
呼び出し側では、同じ要素数の配列を定義して渡してやるだけで、処理が戻ってきたときにはデータが格納されています。
1 2 3 4 5 |
void hoge() { int data[N] = {0}; createData(data); } |
ちなみに、データは自分で用意したものを使いたいよっていう方は、以下のように書き換えればOKです。
1 2 3 4 5 6 7 8 9 10 11 12 13 |
// データ生成関数 void createData(int data[N]) { // ループ変数 int idx = 0; // 決め打ちのデータ int tmp[] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10}; // データを格納 for(idx = 0; idx < N; idx++) { data[idx] = tmp[idx]; } } |
最大を求める
最大値を求める場合、先頭のデータを仮の最大値としておいて、残りの全てのデータと比較をしながら仮の最大値よりも大きいデータが見つかったら仮の最大値を置き換えていくといったことを全てのデータに対して行います。
- 先頭のデータを仮の最大値とする
- 仮の最大値と次のデータを比較する
- 次のデータが仮の最大値より大きい場合、仮の最大値を置き換える
- 終端のデータになるまで2〜3を繰り返す
- 最後に設定されていた仮の最大値が最大データとなる
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 |
// 最大を取得 int max(int data[N]) { // 最大(最初のデータを初期値に設定) int m = data[0]; // ループ変数 int idx = 1; // ループでデータと比較をして最大を求める for(; idx < N; idx++) { // 最大と比較して大きい場合最大データを置き換える if(m < data[idx]) { m = data[idx]; } } // 最大を返却 return m; } |
最小を求める
最小値を求める場合、先頭のデータを仮の最小値としておいて、残りの全てのデータと比較をしながら仮の最小値よりも小さいデータが見つかったら仮の最小値を置き換えていくといったことを全てのデータに対して行います。
- 先頭のデータを仮の最小値とする
- 仮の最小値と次のデータを比較する
- 次のデータが仮の最小値より小さい場合、仮の最小値を置き換える
- 終端のデータになるまで2〜3を繰り返す
- 最後に設定されていた仮の最小値が最小データとなる
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 |
// 最小を取得 int min(int data[N]) { // 最小(最初のデータを初期値に設定) int m = data[0]; // ループ変数 int idx = 0; // ループでデータと比較をして最小を求める for(; idx < N; idx++) { // 最小と比較して小さい場合最小データを置き換える if(m > data[idx]) { m = data[idx]; } } // 最小を返却 return m; } |
平均を求める
平均値を求める場合、全てのデータの合計を算出後に、合計値をデータ数で割ります。
- 全てのデータの合計を計算
- 合計値をデータ数で割る
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 |
// 平均を取得 double avg(int data[N]) { // 合計 int sum = 0; // ループ変数 int idx = 0; // 平均 double a = 0.0; // ループでデータの合計を求める for(; idx < N; idx++) { // 合計にデータを加算 sum += data[idx]; } // データの合計をデータ数で割って平均を求める a = (double)sum / N; // 平均を返却 return a; } |
標準偏差を求める
標準偏差は下式によって求められます。(数式はTeXclipというサイトで画像化させて頂きました)
このとき、xiは一つ一つのデータを表し、averageは平均値を表しています。
そのため、上式が表しているのは、一つ一つのデータから平均値を引いた値の二乗の合計値をデータ数で割ったものの平方根が標準偏差であることを表しています。
- データの平均値を取得
- 一つ一つのデータから平均値を引いたものの二乗の合計を算出
- 2で算出した結果をデータ数で割る
- 3で算出した結果の平方根を取る
ここで、「二乗」「平方根」と何気なく使いましたが、これらはpow関数(べき乗)とsqrt関数で実現することができます。
1 2 3 4 5 6 7 8 9 |
#include <math.h> // pow, sqrt関数を使用するために必要 int hoge() { // 10の二乗を計算 double p = pow(10.0, 2.0); // 10の平方根を計算 double s = sqrt(10.0); } |
平均値を取得する処理は既に上で作成しているので、今回はその処理を流用しています。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 |
// 標準偏差を取得 double std(int data[N]) { // ループ変数 int idx = 0; // 平均 double a = avg(data); // 分散 double sigma = 0.0; // 標準偏差 double s = 0.0; // ループで分散を求める for(; idx < N; idx++) { // (xi - avg)^2を加算 sigma += pow((double)data[idx] - a, 2.0); } sigma /= N; // 分散の平方根を標準偏差として計算 s = sqrt(sigma); // 標準偏差を返却 return s; } |
作った関数を呼び出して出力する
ここまでで各機能を実現するパーツが揃ったので、それらを呼び出した結果を出力してみます。
ついでに、データを乱数で生成しているためはじめにデータも出力するようにしています。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 |
// メイン関数 int main() { // ループ変数 int idx = 0; // データ int data[N] = {0}; // データ生成 createData(data); // 生成したデータを表示 for(idx = 0; idx < N; idx++) { printf("%d ", data[idx]); } printf("\n"); // 最大 int iMax = max(data); // 最小 int iMin = min(data); // 平均 double dAvg = avg(data); // 標準偏差 double dStd = std(data); // 各データを出力 printf("最大 :%d\n", iMax); printf("最小 :%d\n", iMin); printf("平均 :%lf\n", dAvg); printf("標準偏差:%lf\n", dStd); return 0; } |
各機能と上記のコードをくっつけた結果、以下のよう(数値は異なります)に出力されればOKです!
1 2 3 4 5 |
81 32 61 96 69 80 72 81 67 54 最大 :96 最小 :32 平均 :69.300000 標準偏差:16.757386 |
ソースの全文は以下でも確認できます!
まとめ
C言語初学者が質問サイトによく投稿している「C言語で最大、最小、平均、標準偏差を取得する方法」について解説を行いました。
はじめのうちはわからないなりにコピペでもいいと思いますが、可能な限り自分の手で入力した方が身につくので、時間がある方は是非写経してみてください。
他にも、こんな課題の答えだけくれっていうような要望がありましたら、可能な限り答えるつもりでいるので、コメントやTwitterのDMまでご連絡ください。