절사평균, 보정평균에 대해 다룬 문제입니다.
이 문제는 사실 구현 및 수학 문제이니 구현 자체는 쉽습니다.
그런데 왜 이 문제에 대해 포스팅 하려 할까요?
일단 코드 보시고 알려드리도록 하죠.
▶ 입력
첫째 줄에 전체 점수의 개수 N과 제외되는 점수의 개수 K가 빈칸을 사이에 두고 주어진다.
N은 3 이상 100,000 이하의 자연수이다. K는 0 이상 (N/2)-1 이하로 주어진다.
그 다음 N줄에는 각 심판의 점수가 한 줄에 하나씩 주어진다.
점수는 0 이상 10 이하의 실수로 소수점이하 첫째 자리까지 주어진다.
▶ 출력
첫째 줄에 절사평균(N, K)를, 둘째 줄에 보정평균(N, K)를 각각 소수점이하 셋째 자리에서 반올림하여 둘째 자리까지 출력한다.
예를 들어 결과값이 9.667인 경우 9.67로, 5인 경우 5.00으로, 5.5인 경우에는 5.50으로 출력한다.
사용 언어: C++
#include <cstdio>
#include <algorithm>
// 부동소수점의 한계를 인해 발생하는 문제를 없애주기 위함!
#define ERROR 0.00000001
int N, K;
double sc[100002];
double jul() {
double sum = 0;
for(int i = K; i < N - K; ++i) sum += sc[i];
return sum / (N - K * 2);
}
double bo() {
double sum = 0;
sum += sc[K] * K;
for(int i = K; i < N - K; ++i) sum += sc[i];
sum += sc[N - K - 1] * K;
return sum / N;
}
int main() {
scanf("%d%d", &N, &K);
for(int i = 0; i < N; ++i) scanf("%lf", sc + i);
std::sort(sc, sc + N);
printf("%.2lf\n", jul() + ERROR);
printf("%.2lf", bo() + ERROR);
}
보시면 알겠지만 단순히 답만을 낸 것이 아니라 ERROR라는 상수를 통해 값을 조금 바꿔주고 있습니다.
〃그 이유가 뭘까요?〃
바로 C++(혹은 C) 내에서의 부동소수점 연산 때문입니다.
컴퓨터는 정확한 소수를 나타내지 못한다는 사실은 다 아실 겁니다.
그래서 최대한 정확한 비교를 하려 하지만 그럼에도 한계점이 생기기 마련입니다.
double a = 1.0
if( a == 1.0 )
printf("not error");
else
printf("error");
이 코드는 가끔 error를 출력합니다. 이유는
a = 0.9999999······· 혹은 a = 1.000000000000000001 일 때를 다르게 보기 때문입니다.
그래서 컴퓨터는 항상 특정 오차값을 이용해 비교하고자 하는 값과 오차값의 차이가 오차 범위보다 작을 시 참으로 판단하게 됩니다.
double a = 1.0
if( fabs( a - 1.0 ) <= ERROR )
printf("not error");
else
printf("error");
예를 들어 이렇게 말입니다.
그 오차 범위를 확 좁혀주기 위해, 우리는 ERROR라는 충분히 작은 소수를 더해 그 범위 내로 들어가게 해줌으로써 해당 문제를 해결할 수 있게 됩니다.
참고 : Nada님의 답변
저도 처음에는 당연히 맞다고 생각하고 제출을 했지만 틀렸습니다.
그리고 위의 답변을 참고해 겨우 알게 되었습니다.
앞으로 소수점의 비교연산시에는 항상 조심하도록 해야겠다고 생각했습니다.