본문 바로가기

코딩/백준 BOJ

[백준/C언어] 2108번 - 통계학

백준 웹사이트 "2108번 - 통계학" 문제풀이입니다.

언어는 C언어입니다. (제출 언어: C99)

 


문제

 

2108번: 통계학

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

www.acmicpc.net


소스 코드

#include <stdio.h>
#include <math.h>

int arith_mean(int *arr, int N);
int median(int *counting, int N);
int mode(int *counting, int N);
int range(int *counting, int N);

int main(void){
    int N;
    scanf("%d", &N);

    int arr[N];
    for(int i=0; i<N; i++){
        scanf("%d", &arr[i]);
    }

    //정렬(카운팅 정렬)
    int counting[8001]; //0~8000이 -4000~+4000을 의미
    for(int i=0; i<8001; i++){
        counting[i]=0;
    }
    for(int i=0; i<N; i++){
        counting[arr[i]+4000]++;    //0~8000 -> -4000~4000
    }

    //각 통계값 출력
    printf("%d\n", arith_mean(arr, N));
    printf("%d\n", median(counting, N));
    printf("%d\n", mode(counting, N));
    printf("%d\n", range(counting, N));
}

int arith_mean(int *arr, int N){   //산술 평균
    double sum=0;
    for(int i=0; i<N; i++){
        sum += arr[i];
    }
    return (int) round(sum/N);
}

int median(int *counting, int N){   //중앙값
    // N은 홀수: (N+1)/2 번째 수가 중앙값
    int num=0;
    for(int i=0; i<8001; i++){
        num+=counting[i];
        if(num>=(N+1)/2){
            return i-4000;
        }
    }
    return 0;
}

int mode(int *counting, int N){ //최빈값
    //counting 값이 가장 큰 값
    int max=0;  //가장 높은 빈도
    int num=0;  //최빈값의 개수
    int mode=0; //최빈값
    for(int i=0; i<8001; i++){
        if(counting[i]>max){
            max=counting[i];
            num=1;
            mode=i-4000;
        }
        else if(counting[i]==max){
            if(num==1){
                num++;
                mode=i-4000;
            }
            else{   //이미 2개 이상의 최빈값
                num++;
            }
        }
    }
    return mode;
}

int range(int *counting, int N){    //범위
    int max=0;
    int min=0;
    for(int i=8000; i>=0; i--){
        if(counting[i]!=0){
            max=i;
            break;
        }
    }
    for(int i=0; i<8001; i++){
        if(counting[i]!=0){
            min=i;
            break;
        }
    }
    return max-min;
}

문제 풀이

  입력된 수들의 네 가지 기본 통계값을 구하는 코드입니다. main 함수에서는 수들을 입력받아 'arr' 배열에 저장하고, 카운팅 정렬을 시행합니다. 이를 위해 크기 8001의 'counting' 배열을 선언하는데, 이 배열은 0~8000의 인덱스를 가지게 되겠죠? 각각의 인덱스가 -4000~4000을 의미한다고 생각해줍시다. 즉, counting[0]은 값이 -4000인 수의 개수, counting[1]은 값이 -3999인 수의 개수, ... counting[8000]은 값이 4000인 수의 개수가 됩니다. "인덱스 - 4000" 이 해당 카운팅 배열이 의미하는 숫자가 되는 셈이죠. 마지막으로 산술평균, 중앙값, 최빈값, 범위를 각각 계산하는 함수를 호출하여 네 가지 기본 통계값을 구합니다.

  그러면, 각각의 함수를 자세히 한 번 봅시다. 'arith_mean'은 '산술평균'을 계산하는 함수입니다. arith_mean은 'arr'과 'N'을 입력으로 받습니다. arr에 저장된 모든 수들의 합 'sum'을 구한 후, N으로 나누어 산술평균을 구합니다. 이때 소수 첫째짜리에서 반올림하기 위해, <math.h>의 'round' 함수를 이용합니다. 'round' 함수의 입력, 출력이 모두 double형이므로, 이에 알맞게 자료형 변환을 시킵니다.

  'median'은 '중앙값'을 계산하는 함수입니다. median은 'counting'과 'N'을 입력으로 받습니다. N은 홀수이므로, (N+1)/2 번째로 작은 수가 됩니다. 정렬된 'counting' 배열은 중앙값 구하는 것을 매우 편리하게 해주는데, 각 수의 개수가 기록되어 있기에 가장 작은 수부터 개수를 기록하여 (N+1)/2 번째 수에 도달하면 그 값을 반환합니다. 참고로 Line 51의 'return 0'은 해주지 않아도 return이 항상 보장되기에 무관하지만, 컴파일러 쪽에서 warning이 뜰 수 있어 해주는 것이 좋습니다.

  'mode'는 '최빈값'을 계산하는 함수입니다. 최빈값은 곧 counting 배열의 값이 가장 높은 수를 찾는 것인데, 최빈값이 여러 개일 경우 두 번째로 작은 값을 구하는 것이 조금 복잡하게 만듭니다. 세 변수 'max', num', 'mode'를 선언하여, '가장 높은 빈도', '최빈값의 개수', '최빈값'이란 의미를 부여합니다. 나머지는 소스코드를 보고 따라가면 쉽게 이해가 될 것입니다.

  마지막으로 'range'는 '범위'를 계산하는 함수입니다. 최댓값 'max'와 최솟값 'min'을 counting 배열로 구하고, 둘의 차를 반환하면 원하는 범위가 얻어집니다.

반응형