문제 출처: https://www.acmicpc.net/problem/2108
수를 처리하는 것은 통계학에서 상당히 중요한 일이다. 통계학에서 N개의 수를 대표하는 기본 통계값에는 다음과 같은 것들이 있다. 단, N은 홀수라고 가정하자.
산술평균 : N개의 수들의 합을 N으로 나눈 값
중앙값 : N개의 수들을 증가하는 순서로 나열했을 경우 그 중앙에 위치하는 값
최빈값 : N개의 수들 중 가장 많이 나타나는 값
범위 : N개의 수들 중 최댓값과 최솟값의 차이
N개의 수가 주어졌을 때, 네 가지 기본 통계값을 구하는 프로그램을 작성하시오.
예제 입력
5
1
3
8
-2
2
예제 출력
2
2
1
10
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.Arrays;
public class Main {
public static void main(String [] args) throws IOException {
BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
StringBuilder sb = new StringBuilder();
int total = Integer.parseInt(br.readLine());
int [] arr = new int[total];
double avg = 0;
for(int i = 0; i < total; i++) {
arr[i] = Integer.parseInt(br.readLine());
avg += arr[i];
}
avg /= total;
//산술평균
avg = Math.round(avg);
int av = (int) avg;
sb.append(av).append("\n");
Arrays.sort(arr);
//중앙값
sb.append(arr[total / 2]).append("\n");
// 최빈값 구하기
int [] frequency = new int [4001];
int [] negFrequency = new int [4001];
for(int elem : arr) {
if(elem < 0)
negFrequency[Math.abs(elem)]++;
else
frequency[elem]++;
}
int max = 0;
int num = 0;
int second = num;
for(int i = negFrequency.length - 1; i > 0; i--) {
if(negFrequency[i] > max){
max = negFrequency[i];
num = -i;
second = num;
}
}
for(int i = 0; i < frequency.length; i++) {
if(frequency[i] > max){
max = frequency[i];
num = i;
second = num;
}
}
int minusEliminatedIndex = num < 0 ? -num : num;
if(num < 0) {
for (int i = minusEliminatedIndex - 1; i > 0; i--) {
if (negFrequency[i] == max) {
second = -i;
break;
}
}
}
if(second != num)
sb.append(second).append("\n");
else {
int x = num < 0 ? 0 : num + 1;
for(int i = x; i < frequency.length; i++) {
if(frequency[i] == max){
second = i;
break;
}
}
sb.append(second).append("\n");
}
//범위
sb.append(Math.abs(arr[total - 1] - arr[0])).append("\n");
System.out.println(sb);
}
}
평균, 중앙값, 범위 구하는것은 어렵지 않으나 최빈값을 구하는 부분이 좀 생각을 해야하는 문제이다.
내가 구한 방법은
숫자별 빈도를 구하기 위해서 배열을 음수와 양수 용으로 하나씩 만든후 빈도를 기록을한다.
negative Frequency[4001]
positive Frequency[4001]
기록 후 최빈값이 음수이면 중복값을 찾기 위해 음수 빈도 배열을 확인한다.
index를 설정하는게 관건인데 예를 들어 -10이 최빈값이면 negative Frequency 배열의 인덱스 10 부터 1까지를 스캔한다. 이런식으로 음수는 거꾸로 스캔을하고 양수는 오름차순으로 스캔하면 된다.
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
public class Main {
public static void main(String[] args) throws IOException{
BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
StringBuilder sb = new StringBuilder();
int N = Integer.parseInt(br.readLine());
int[] countSort = new int[8001]; // 넣은 숫자들, 최빈값
int median = 100000; // 중앙값
int max = Integer.MIN_VALUE; // 최소값
int min = Integer.MAX_VALUE; // 최대값 범위
int sum = 0;
int range = 0;
int count = 0;
double avg = 0.0;
int mode_max = 0;
int mode_value = 10000;
boolean flag = false;
for (int i = 0; i < N; i++) {
int input = Integer.parseInt(br.readLine());
countSort[input + 4000]++; // 4000이 기준(==0)
sum += input;
if(input > max) {
max = input;
}
if(input < min) {
min = input;
}
}
// 산술평균
avg = Math.rint((double)sum / N);
for (int i = min + 4000; i <= max + 4000; i++) { // counting sort는 인덱스가 값이니까
if(countSort[i] > 0) {
// 중앙값
if(count <(N + 1) / 2) {
count += countSort[i];
median = i -4000;
}
// 최빈값
// 중복이 없는 경우
if(countSort[i] > mode_max){
mode_max = countSort[i];
mode_value = i - 4000;
flag = true;
}
// 중복되는 경우
else if(mode_max == countSort[i] && flag == true) {
mode_value = i - 4000;
flag = false;
}
}
}
// 범위
range = max + min*-1;
sb.append((int)avg).append("\n");
sb.append(median).append("\n");
sb.append(mode_value).append("\n");
sb.append(range).append("\n");
System.out.println(sb);
}
}
8000으로 frequency 배열을 정하고 4000을 더한값으로 index를 찾아 내었다. 생각은 했지만 조금 직관적으로 와닿지 않아서 이렇게 안했는데 더 나은 방법인것 같다.