#78. 백준 1427번 문제 풀이: 통계학 문제 원본 보기
통계학에서 N개의 수를 대표하는 기본 통계값들은 다음과 같다. 단, N은 홀수. 1.산술평균 : 수들의 합을 N으로 나눈 값 2.중앙값 : 수들을 증가하는 순서로 나열했을 경우 그 중앙에 위치하는 값 3.최빈값 : 수들 중 가장 많이 나타나는 값 4.범위 : 수들 중 최댓값과 최솟값의 차이 N개의 수가 주어졌을 때, 네 가지 기본 통계값을 구하는 프로그램을 작성하시오. 입력: 첫 줄에 수의 개수 N(1≤N≤500,000, N은 홀수). 그 다음 N개의 줄에는 정수들이 주어진다. 정수의 절댓값은 4,000을 넘지 않는다. 출력: 첫 줄에는 산술평균을 출력. 소수점 이하 첫째 자리에서 반올림. 둘째 줄에는 중앙값. 셋째 줄에는 최빈값. 여러 개 있을 때에는 최빈값 중 두 번째로 작은 값을 출력. 넷째 줄에는 범위. * -0.3333 은 반올림하면 0이다. -0이 아닌 0으로 출력해야 한다.
입력/출력
--입력--
5
1
3
8
-2
2
--출력--
2
2
1
10
--입력--
1
4000
--출력--
4000
4000
4000
0
--입력--
5
-1
-2
-3
-1
-2
--출력--
-2
-2
-1
2
--입력--
3
0
0
-1
--출력--
0
0
0
1
문제풀이+해설
프로그램은 다음과 같은 순서로 작성하면 된다.
1. 입력된 값들을 배열에 저장한다.
2. 값들을 모두 더하고 나누어 산술평균을 출력한다.
3. 숫자 배열을 소팅을 한다.
4. 소팅된 배열에서 중간 N/2 번째 배열값(중앙값)을 출력한다.
5. 최빈값을 구해서 출력(요것은 밑에 자세히 설명)
6. 범위는 소팅된 배열에서 마지막 배열값(최댓값)에서 첫번째 배열값(최솟값)을 빼주어 출력한다.

최빈값을 구하기 이해 각 숫자들의 빈도값을 알아야 한다.
조건에서 숫자의 최대값이 4000을 넘지 않는다고 했다.
즉, 숫자의 값을 배열의 인덱스로 간주하고 배열에 카운트값을 넣는 알고리즘을 이용하라는 의미이다.
숫자의 범위가 -4000 ~ +4000 까지 이므로 8000개의 배열로 숫자 인덱스를 사용할 수 있다.
배열 인덱스는 0 부터 이므로 -값을 사용하기 위해 인덱스 4000 배열을 숫자 0으로 간주하면 된다.
즉 ns_count[4000] 값이 0의 빈도수로 보는 것이다. ns_count[40001]은 1의 빈도수....


파이썬의 경우 Counter 를 사용하면 최빈값 구하는 루틴 없이 결과값을 쉽게 얻어 올 수 있다.
파이썬에서도 배열을 이용한 루틴을 구현하려면 C나 C++ 소스를 참조하면 구현가능하다.
code sol.
#include <stdio.h>
#include <stdlib.h>
#include <math.h>

int compare(const void *a, const void *b) { // sort 함수에 사용할 정렬 함수
    int na = *(int *)a;
    int nb = *(int *)b;

    if (na < nb) return -1;
    if (na > nb) return 1;
    return 0;
}

int main() {
  int N; // 입력할 숫자 개수
  int ns[500000]; // 입력된 숫자를 저장할 배열
  int ns_count[8001] = {0}; // 각 숫자의 빈도수 카운트 저장용 배열 선언 후 0으로 초기화.
  double sum = 0; // 평균을 내기 위해 숫자들의 합을 저장할 변수. 값이 커질 수 있으므로 double로.
  int max_count_d = 0, max_count_n = 0, max_count_v = 0; // 최빈값 중북횟수, 최빈값, 최빈값 카운터 저장할 변수.
  int i;

  scanf("%d", &N); // 입력할 숫자 개수값을 입력 받음
  for(i = 0; i < N; i++) { // 지정한 개수만큼 반복해서 입력을 받음
    scanf("%d", &ns[i]);
    sum += ns[i]; // 속도를 위해 입력받으면서 합을 미리 계산함.
  }
  
  // 1.산술평균 출력
  printf("%d\n", (int)round(((sum / N)))); // 더한값을 N으로 나누고 반올림.

  // 2.중앙값 출력
  qsort(ns, N, sizeof(int), compare);  // 오른차순으로 정렬
  printf("%d\n", ns[N / 2]); // 정렬된 수에서 중간에 있는 값을 출력

  // 3.최빈값 출력
  for(i = 0; i < N; i++) { // 숫자별 빈도수를 배열로 카운트한다.
    ns_count[ns[i] + 4000]++; // 4000 인덱스가 숫자 0을 의미. 숫자값에 해당하는 인덱스 값 증가
  }
  for(i = 0; i <= 8000; i++) {
    if(max_count_v <= ns_count[i]) { // 빈도수가 더 높거나 같으면 최빈값으로 대입
      if(max_count_v == ns_count[i]) max_count_d++;// 값이 같으면 중복횟수 기록
      else max_count_d = 0; // 같지 않으면 중복횟수 0.
      max_count_n = i; // 최빈값 저장
      max_count_v = ns_count[i]; //최빈 빈도수 저장 
    }
  }
  if(max_count_d == 0) { // 최빈값이 하나만 존재하므로..
    printf("%d\n", max_count_n - 4000); // 최빈값이 하나면 그대로 출력
  } else { // 같은 최빈값이 존재하므로 두분째 작은 최빈값을 찾음
    max_count_d = 0; // 반복값 체크용으로 다시 사용.
    for(i = 0; i <= 8000; i++) {
      if(ns_count[i] == max_count_v) { // 최빈값하고 같으면
        if(max_count_d == 1) { // 첫번째 값이 있었다면 두번째 이므로 출력
          printf("%d\n", i - 4000); // 두번째 작은 최빈값 출력
          break; // 루틴을 끝냄
        } else max_count_d++; // 중복 최빈값 순서 체크
      }
    }
  } 

  // 4.범위 출력 : 정렬되어 있으므로 배열 마지막값이 최댓값, 처음값이 최솟값
  printf("%d", ns[N - 1] - ns[0]); // 최대값에서 최솟값을 빼서 출력
  return 0;
}
© 코드솔 - CodeSol. All Rights Reserved.