Algorithms/백준

[백준] '통계학' - Java

LEFT 2023. 7. 18. 13:18

<문제 제시>

<문제설명>
수를 처리하는 것은 통계학에서 상당히 중요한 일이다. 
통계학에서 N개의 수를 대표하는 기본 통계값에는 다음과 같은 것들이 있다. 
단, N은 홀수라고 가정하자.

산술평균 : N개의 수들의 합을 N으로 나눈 값
중앙값 : N개의 수들을 증가하는 순서로 나열했을 경우 그 중앙에 위치하는 값
최빈값 : N개의 수들 중 가장 많이 나타나는 값
범위 : N개의 수들 중 최댓값과 최솟값의 차이
N개의 수가 주어졌을 때, 네 가지 기본 통계값을 구하는 프로그램을 작성하시오.

<예시 입출력>


<문제 해결 과정>

요구하는 산술평균, 중앙값, 최빈값, 범위를 구하는 문제이다. 나머지는 간단한 연산으로 구할 수 있다고 해도 최빈값을 구하는 부분에서 생각이 많아졌다. 

Try 1) 

BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
int N = Integer.parseInt(br.readLine());

int[] arr = new int[N];
int sum = 0;

for(int i = 0; i < N; i++) {
    arr[i] = Integer.parseInt(br.readLine());
    sum += arr[i];
}

// 1차 테스트 출력
System.out.println(Arrays.toString(arr));

// 1. 산술평균 구하기
int ave = sum / N;
System.out.println("산술평균 = " + ave);

// 3. 최빈값 구하기
int num = 0;
int[] test = new int[8001];

for(int i = 0; i < 8001; i++) {
    for(int j = 0; j < arr.length; i++) {
        if(!test[i]) {
            int data = arr[j];
            test[data] = true;
        }
        else {
            num = i;
        }
    }

}
System.out.println("최빈값 = " + num);

// 2. 중앙값 구하기
int mid = 0;
Arrays.sort(arr);
mid = arr[N / 2];
System.out.println("중앙값 = " + mid);

// 4. 범위 구하기
int ran = arr[arr.length - 1] - arr[0];
System.out.println("범위 = " + ran);

boolean배열로 최빈값을 찾으려했으나, 인덱스 범위 오류가 발생하였다.


Answer 1) 

// 3. 최빈값 구하기
int num = 0;
int[] test = new int[8001];


////
int[] plus = new int[4002];
int[] minus = new int[4001];

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

    // 값이 음수일 경우, 절대값을 해준 후 minus배열에 체크
    if(arr[i] < 0) {
        minus[Math.abs(arr[i])]++;
    }

    else {
        plus[arr[i]]++;
    }
}

List<Integer> list = new ArrayList<>();

// 가장 높은 빈도수를 갱신
int max = 0;
for(int i = 0;i < plus.length; i++) {
    max = Math.max(max,  plus[i]);
}

for(int i = 0;i < minus.length; i++) {
    max = Math.max(max,  minus[i]);
}

for(int i : arr) {
    // 값이 음수일 경우
    if(i < 0) {
        // 값을 절대값처리한 것이 max에 담은 값과 같고, 리스트에서도 i를 포함하고 있지 않으면,
        if(minus[Math.abs(i)] == max && !(list.contains(i))) {
            list.add(i);
        }
    }

    else {
        if(plus[Math.abs(i)] == max && !(list.contains(i))) {
            list.add(i);
        }
    }
}

// 만약 최빈값이 두개이상으로 겹친다면, 2번째로 작은 것을 출력
if(list.size() >= 2) {
    num = list.get(1);
}
else {
    num = list.get(0);
}
System.out.println("최빈값 = " + num);

먼저 입력되는 값의 범위가 주어졌기 때문에 반을 나누어 양수부분과 음수부분의 배열로 나눈다.

값이 음수이면 그 해당하는 값을 절대값해준 후 음수 배열에 ++ (개수체크)해주면 해당 값의 인덱스 위치에 따라 

값이 증가한다. else문으로는 양수 배열에 추가해준다.

Math.max로 plus와 minus 배열에서의 개수가 가장 많은 값을 체크한다. (최빈값 유력)

for-each문으로 처음 값들을 가져와 음수일 경우

1) 그 가져온 값을 절대값 체크한 것과 max가 같고
2) 리스트에서도 가져온 값을 포함하고 있지 않으면 (contains() 사용)
>> 리스트에 추가한다.

- 만약 최빈값이 2개 이상 (ex. 3, 3) 으로 겹친다면,
>> list.size() >= 2 로 체크가능 (=최빈값을 담은 리스트의 크기가 2이상이면 최빈값이 2개 이상인 것)
>> list.get(1)로 두번째로 작은 값을 출력
>> else { num = list.get(0) };


<전체코드>

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;

public class Main {

	public static void main(String[] args) throws IOException {
		BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
		int N = Integer.parseInt(br.readLine());
		
		int[] arr = new int[N];
		double sum = 0;

		for(int i = 0; i < N; i++) {
			arr[i] = Integer.parseInt(br.readLine());
			sum += arr[i];
		}
		
		// 1차 테스트 출력
		System.out.println(Arrays.toString(arr));
		
		// 1. 산술평균 구하기
		double ave = Math.round((double)sum / N);
		System.out.println("산술평균 = " + (int)ave);
		
		// 2. 중앙값 구하기
		int mid = 0;
		Arrays.sort(arr);
		mid = arr[N / 2];
		System.out.println("중앙값 = " + mid);
		
		// 3. 최빈값 구하기
		int num = 0;
		int[] test = new int[8001];
		
		
		////
		int[] plus = new int[4002];
		int[] minus = new int[4001];
		
		for(int i = 0; i < N; i++) {
			
			// 값이 음수일 경우, 절대값을 해준 후 minus배열에 체크
			if(arr[i] < 0) {
				minus[Math.abs(arr[i])]++;
			}
			
			else {
				plus[arr[i]]++;
			}
		}
		
		List<Integer> list = new ArrayList<>();
		
		// 가장 높은 빈도수를 갱신
		int max = 0;
		for(int i = 0;i < plus.length; i++) {
			max = Math.max(max,  plus[i]);
		}
		
		for(int i = 0;i < minus.length; i++) {
			max = Math.max(max,  minus[i]);
		}
		
		for(int i : arr) {
			// 값이 음수일 경우
			if(i < 0) {
				// 값을 절대값처리한 것이 max에 담은 값과 같고, 리스트에서도 i를 포함하고 있지 않으면,
				if(minus[Math.abs(i)] == max && !(list.contains(i))) {
					list.add(i);
				}
			}
			
			else {
				if(plus[Math.abs(i)] == max && !(list.contains(i))) {
					list.add(i);
				}
			}
		}
		
		// 만약 최빈값이 두개이상으로 겹친다면, 2번째로 작은 것을 출력
		if(list.size() >= 2) {
			num = list.get(1);
		}
		else {
			num = list.get(0);
		}
		System.out.println("최빈값 = " + num);
		
		// 4. 범위 구하기
		int ran = arr[arr.length - 1] - arr[0];
		System.out.println("범위 = " + ran);
		
		// 최종 출력
		System.out.println((int)ave);
		System.out.println(mid);
		System.out.println(num);
		System.out.println(ran);

	}
}

최빈값을 구하는 부분이 핵심인 문제였다. 하지만 최빈값에서도 최빈값이 2개이상이라는 조건과 같이 예외사항이 분명 존재하기때문에 모두 만족하는 코드를 작성하는 것이 목표였다.


문제링크)
https://www.acmicpc.net/problem/2108

 

2108번: 통계학

첫째 줄에 수의 개수 N(1 ≤ N ≤ 500,000)이 주어진다. 단, N은 홀수이다. 그 다음 N개의 줄에는 정수들이 주어진다. 입력되는 정수의 절댓값은 4,000을 넘지 않는다.

www.acmicpc.net