Java:合計、平均、標準偏差、中央値、最頻値を求める [勉強]
「元気もりもり木曜日」のハズが、頭痛が痛い(?)からおでこに冷えピタ貼ってる しゃくれアゴです。
前のブログ記事の続きで、配列の演習問題です。
問題9-5:(class Statistics)
「0~100の整数をランダムに200件、発生させ、下記を行うプログラムを作成せよ」
(1) 全ての値を出力
(2) 平均を出力
(3) 標準偏差を出力
(4) メジアン(中央値)を出力
(5) モード(最頻値)を出力
これねぇ・・・。
全ての値を出力、平均を出力くらいは、すぐできますよ。
標準偏差とか、中央値とか、最頻値は・・・検索して、それぞれどうやって求めるのかを理解したうえでプログラム書かないといけないんですよね。(^-^;
とりあえず、私なりの回答ソースは、以下の通りです。
public class Statistics {
public static void main(String[] args) {
int[] num;
int length = 200;
int total = 0;
double ave = 0.0;
double bunsan = 0.0;
double hyoujun = 0.0;
double mid = 0.0;
num = new int[length];
for (int i=0; i<length; i++) {
num[i] = (int)(100.0 * Math.random()); //1~100の乱数生成し代入
}
//1.配列内のすべての値を表示
System.out.println("200件の乱数をすべて表示");
for (int i=0; i<length; i++) {
System.out.print(num[i] + " ");
if (i%20 == 0) {
System.out.println();
}
}
System.out.println();
//2.平均を出力
for (int i=0; i<length; i++) {
total = total + num[i];
}
ave = total/length;
System.out.println("平均は" + ave);
//3.標準偏差を出力
total = 0;
for (int i=0; i<length; i++) {
total += (num[i]-ave)*(num[i]-ave);
}
bunsan = total /length;
hyoujun = Math.sqrt(bunsan);
System.out.println("標準偏差は" + hyoujun);
//昇順ソート
for (int i=0; i<length-1; i++) {
for (int j=i+1; j<length; j++) {
if (num[i] > num[j]) {
int tmp = num[i];
num[i] = num[j];
num[j] = tmp;
}
}
}
//4.中央値算出
switch (length % 2) {
case 0:
System.out.println("中央値は" + num[(length/2)+1]);
break;
case 1:
System.out.println("中央値は" + num[length/2]);
break;
}
// System.out.println();
//ソート後の配列表示
// System.out.println("昇順ソート後を表示");
// for (int i = 0; i < length; i++) {
// System.out.print(num[i] +" ");
// if (i % 10 == 0) {
// System.out.println();
// }
// }
// System.out.println();
//5.最頻値の表示
int[] data = new int[length];
for (int i = 0; i < length; i++) {
for (int j = 0; j < length; j++) {
if (num[i] == num[j]) {
data[i]++;
}
}
}
//0~100が何回出力されたかカウントして表示
// System.out.println("0~100の出力回数");
// for (int i = 0; i < length; i++) {
// System.out.print(num[i] + ":" + data[i] + " ");
// if (i % 10 == 0) {
// System.out.println();
// }
// }
// System.out.println();
//
int max = 0;
for (int i = 0; i < length; i++) {
if(data[i] > max) {
max = data[i];
}
}
// System.out.println("max:" + max);
for (int i = 0; i < length; i++) {
if (data[i] == max) {
System.out.println("最頻値は" + num[i]);
}
if (data[i] == max) {
break;
}
}
// int mod = getMode(num);
// System.out.println("最頻値は" + mod);
}
// public static int getMode(int[] x) {
// int[] work =(int[])x.clone();
// //昇順ソート
// for (int i =0; i < x.length-1; i++) {
// for (int j = i + 1; j < x.length; j++) {
// if (work[i] > work[j]) {
// int tmp = work[i];
// work[i] = work[j];
// work[j] = tmp;
// }
// }
// }
// int mNumber = 0;
// int mCount = 0;
// int cnt = 0;
// for (int i = 0; i < work.length; i++) {
// for (int j = 0; j < work.length; j++) {
// if (work[i] == work[j]) {
// cnt++;
// } else {
// break;
// }
// if (cnt > mCount) {
// mNumber = 1;
// mCount = cnt;
// }
// }
// }
// return work[mNumber];
// }
}
いろいろコメントアウトされている行がたくさんありますが・・・試行錯誤の結果だと思ってください。
これでもまだ、最後の最頻値の部分で、「最頻値はXX」という出力が複数実行されます。最頻値が5個だった場合、5回出力されるんですねぇ。
さらに、最頻値が複数ある場合、全部表示するんですよ。
まだまだ手を加える余地があるんですけど、ここでギブアップというか・・・。これ以上、私の頭をしぼっても何も出ませんというか・・・・・・。
数学的なことをするプログラムって難しいですねぇ。(^-^;
本日(08/12)はここまで。
次回は第10章「オブジェクト指向に向けて」の練習問題です。
さて、REDSTONEの定期メンテも終わっているだろうし、REDSTONEで遊ぶべ。
----以下、2010年08月22日20:40頃追記----
講師の回答ソースもUPしておきます。
どうも、私のソースは問題があるというか、あまり正しくなかったようですね。特に最頻値のところ。
ということで・・・、以下、講師回答ソースです。
public class Statistics
{
/**
* メインメソッド
* 各演算の結果を表示する
* main 実行メソッド
*/
public static void main(String[] args)
{
// 要素数200の配列を作成・データ代入
int[] intAry = getArray(200);
// 配列の内容を表示
printArray(intAry);
System.out.println("\n\n");
// 平均値を表示
System.out.println("平均値は " + getAverage(intAry) + "です。");
// 標準偏差を表示
System.out.println("標準偏差は " + getDeviationValue(intAry) + "です。");
// 中央値を表示
System.out.println("中央値は " + getMedium(intAry) + "です。");
// 最頻値を表示
System.out.print("最頻値は ");
printArray(getMode(intAry));
System.out.println("です。");
}
/**
* 指定された要素数の配列を作成し、内容をランダムな値を格納して返すメソッド
* @param intIndex 作成する配列数
* @return ランダムな値を引数の数だけ格納した配列
*/
public static int[] getArray(int intIndex)
{
// 指定された要素数の配列を作成
int[] intAry = new int[intIndex];
// 配列にランダムに作成した0~100の整数を格納
for(int i = 0; i < intIndex ; i++)
{
intAry[i] = (int)(Math.random() * 101) + (int)Math.round(Math.random());
}
return intAry;
}
/**
* 配列の内容を全て表示するメソッド
* @param intAry 表示する配列
*/
public static void printArray(int[] intAry)
{
// 要素数の数だけ繰り返す
for(int i = 0; i < intAry.length; i++)
{
// 10個ごとに改行
if(i % 10 == 0 && i != 0)
{
System.out.println();
}
// 内容を表示
System.out.print(intAry[i] + "\t");
}
}
/**
* 平均値を返すメソッド
* @param intAry 平均値を計算する配列
* @return 平均値
*/
public static double getAverage(int[] intAry)
{
int intSum = 0; // 合計値
// 要素数の数だけ繰り返す
for(int i = 0; i < intAry.length; i++)
{
intSum += intAry[i];
}
return (double)intSum / intAry.length;
}
/**
* 標準偏差を返すメソッド
* 求め方:[(各要素-平均値)^2 の合計]÷要素数の平方根
* @param intAry 標準偏差を計算する配列
* @return 標準偏差
*/
public static double getDeviationValue(int[] intAry)
{
// 平均値を取得
double dblAve = getAverage(intAry);
double dblSum = 0.0;
// 要素数の数だけ繰り返す
for(int i = 0; i < intAry.length; i++)
{
// (平均値-値)^2を足していく
dblSum += Math.pow((dblAve - intAry[i]), 2.0);
}
// 標準偏差を返す
return Math.sqrt(dblSum / intAry.length);
}
/**
* メジアン(中央値)を返すメソッド
* @param intAry メジアンを計算する配列
* @return メジアン
*/
public static double getMedium(int[] intAry)
{
printArray(intAry);
// 配列の内容をソートする
// 先頭から順に比較する
for(int i = 0; i < intAry.length - 1; i++)
{
// 比較元の1つ次の要素から最後の要素までを比較する
for(int j = i + 1; j < intAry.length; j++)
{
// 比較元の要素より小さいものがあれば内容を交換
if(intAry[j] > intAry[i])
{
int temp = intAry[i];
intAry[i] = intAry[j];
intAry[j] = temp;
}
}
}
// 要素数が奇数か偶数か
if(intAry.length % 2 == 1)
{
// 要素数が奇数の場合は真ん中の値を返す
return intAry[intAry.length / 2];
}
else
{
// 要素数が偶数の場合は真ん中2つの値の平均値を返す
return (intAry[intAry.length / 2] + intAry[intAry.length / 2 - 1]) / 2.0 ;
}
}
/**
* モード(最頻値)を返すメソッド
* @param intAry モードを計算する配列
* @return モード
*/
public static int[] getMode(int[] intAry)
{
/*
* 最大値と最小値を出し、
* その範囲で出現回数をカウントし、
* モードを探す。
*/
int intMax = intAry[0]; // 最大値
int intMin = intAry[0]; // 最小値
// 最大値、最小値を判定
for(int i = 1; i < intAry.length; i++)
{
if(intAry[i] > intMax)
{
intMax = intAry[i];
}
else if(intAry[i] < intMin)
{
intMin = intAry[i];
}
}
// 各要素内容の出現回数をカウントする
int[] intCount = new int[intMax+1]; // intCountの添え字は値を使用
// 最小値から最大値の値の間ループ
for(int i = intMin; i <= intMax; i++)
{
// 配列を全て探す
for(int j = 0; j < intAry.length; j++)
{
if(intAry[j] == i)
{
intCount[i]++;
}
}
}
// 出現回数が一番多い要素を選ぶ
int intMaxIndex = intCount[0]; // 最多の出現回数
int intCountMax = 1; // 最多の数が同じものの数
for(int i = 1; i < intCount.length; i++)
{
// 現在の最多の数より大きいものがあれば置換
if(intCount[i] > intMaxIndex)
{
intMaxIndex = intCount[i];
// 置換したら同じものの数を初期化
intCountMax = 1;
}
else if(intCount[i] == intMaxIndex)
{
// 最多の数と同じものがあれば、最多の数が同じものの数を増やす
intCountMax++;
}
}
// 出現回数が一番多い要素を格納する配列を作成
int[] intMaxAry = new int[intCountMax];
// 格納用の配列の要素番号
int intIndex = 0;
// 要素を格納
for(int i = 0; i < intCount.length; i++)
{
// 最多の数と同じ数であれば、返却用の配列に格納
if(intCount[i] == intMaxIndex)
{
intMaxAry[intIndex] = i;
intIndex++;
}
}
// 出現回数が一番多い要素を格納した配列を返却
return intMaxAry;
}
}
すごいですねぇ。
最頻値が複数あった場合でも、ちゃんと対応できていますねぇ。
こういうソースが書けるように頑張るべ。
javaはあまり詳しくないので、javaで最頻値を求めるライブラリを検索していたら、このページが先頭に来たので・・・・
講師の方の模範回答も学習のためかとは思いますが、さらに改良点があると思います。
1.Sortが・・・バブルソート
学習のためだとは思いますが、実装するならクイックソート位は・・・
javaの標準ライブラリのsortはマージソートですし、ソートが本題でないのなら、標準ライブラリをつかうべきかと。
ソートだけでも結構面白いので、教科書を探して読んでおいたほうがいいですよ。
2.最頻値を検出するためのループが二回
メモリ効率が良くないわりに、二回ループ回しているのは、意味がないかと。
仮の最頻値のリストを作りつつ、現状よりも多い頻度で現れたデータがあれば、その時点でのリストをクリアすればよいのでは?
あとは、コレクションライブラリを全く使わないのは学習のためでしょうか。
以下、他にも検索でこのページにたどり着かれた人用に、
少し実用化した最頻値を求めるコードを書いておきます。
非常に簡単に書いているので、コメントなしで読める範囲かとおもい、あえてほとんどコメントをいれませんでした。
public class Statistics {
public static void main(String[] args) {
int[] data = getData(200);
Arrays.sort(data);
System.out.println(Arrays.toString(data));
System.out.println(getMedium(data));
}
private static int[] getData(int n){
int[] data = new int[n];
Random r = new Random();
for (int i = 0; i < n; i++) {
data[i] = r.nextInt(100);
}
return data;
}
private static List<Integer> getMedium(int[] data) {
List<Integer> list = new LinkedList();
int count = 0;
int maxcount = -1;
int prev = Integer.MIN_VALUE; // 値は門番。dataがとりえない値なら何でもよい
for (int val : data) {
if (val == prev) {
count++;
} else {
if (count > maxcount) {
list.clear();
maxcount = count;
}
if (count == maxcount) {
list.add(prev);
}
count = 0;
}
prev = val;
}
return list;
}
}
Blog主さんはSEとして訓練中でしょうか。
技術者として良い仕事ができるよう、お互い研鑽に励みましょう。
by とおりすがり (2011-02-27 01:38)
インデントがなくなってしまったので再投稿と、
一部修正。2回目のcount=0;はcount=1;であるべきですね。
prevと今のvalueが違うので、1個めと数えるべきだし。
初期化時に入れているcoun=0;は門番なので、0個であってます。
public class Statistics {
public static void main(String[] args) {
int[] data = getData(200);
Arrays.sort(data);
System.out.println(Arrays.toString(data));
System.out.println(getMedium(data));
}
private static int[] getData(int n){
int[] data = new int[n];
Random r = new Random();
for (int i = 0; i < n; i++) {
data[i] = r.nextInt(100);
}
return data;
}
private static List<Integer> getMedium(int[] data) {
List<Integer> list = new LinkedList();
int count = 0;
int maxcount = -1;
int prev = Integer.MIN_VALUE; // 値は門番。dataがとりえない値なら何でもよい
for (int val : data) {
if (val == prev) {
count++;
} else {
if (count > maxcount) {
list.clear();
maxcount = count;
}
if (count == maxcount) {
list.add(prev);
}
count = 1;
}
prev = val;
}
return list;
}
}
by とおりすがり (2011-02-27 13:36)
あー、getMediumでなくて、getModeですね。
自宅で書き直した時に間違えました。お恥ずかしい。
何度も投稿するのもアレなので、getModeに読み替えていただければ。
by とおりすがり (2011-02-27 13:40)
>>とおりすがりさん
コメント&修正ソース、ありがとうございます。
職業訓練でJavaを勉強したものの、いまの職場では全然活かせていません。(^-^;
いまはBtoB製品のバージョンアップに伴うシステム構築試験をやってます。
これもSEの仕事のひとつなのかなぁ??
そもそも、SEって何だろうって思う、今日この頃でした。(^-^;
by しゃくれアゴ (2011-02-27 17:05)