{ }codelper
실수 나눗셈제곱근P0168

센서 값의 평균과 표준편차

공장의 센서들이 같은 시간 동안 잰 값을 모았다. 센서마다 값의 평균과 표준편차를 구해 값이 얼마나 흔들렸는지 본다. 첫 줄에 센서 수 s가 주어진다. 다음 s줄에 센서마다 값의 개수 n과 정수 값 n개가 빈칸으로 나뉘어 한 줄에 주어진다. 평균 m은 값의 합을 n으로 나눈 실수다. 표준편차는 값마다 (값 - m)을 제곱해 모두 더하고, 그 합을 n으로 나눈 다음 제곱근을 구한 것이다(n - 1이 아니라 n으로 나눈다). 센서마다 한 줄에 평균과 표준편차를 소수 셋째 자리까지 반올림해 빈칸 하나로 띄워 입력 차례대로 출력하라. 정수로 떨어져도 소수 세 자리를 쓴다(80 → 80.000). 반올림이 경계에 걸리지 않도록, 평균이나 표준편차에 1000을 곱한 값의 소수 부분이 0.49 이상 0.51 이하인 입력은 주어지지 않는다.

입력
1 ≤ s ≤ 20, 1 ≤ n ≤ 1,000, 0 ≤ 값 ≤ 1,000,000,000, 평균·표준편차 × 1000의 소수 부분이 0.49~0.51인 입력은 없다
출력
센서마다 한 줄에 평균과 표준편차(소수 셋째 자리까지)
예시 1 · stdin
4
5 70 80 90 85 75
1 1000000000
4 999999999 1000000000 1000000001 1000000000
3 1 2 2
stdout
80.000 7.071
1000000000.000 0.000
1000000000.000 0.707
1.667 0.471
main.py
입력(stdin)

실행하면 이 줄들이 차례로 input()·표준 입력으로 들어가요(실행 중에 묻지 않아요).

$ 준비됐어요

코드를 입력해 풀려면 PC에서 열어 주세요. 모바일에서는 문제와 풀이를 볼 수 있어요.

다른 언어로는?언어별 풀이 보기 · 8개 언어 풀이

나눗셈에서 첫째 함정이 나와요.
Python 3의 /, JS·TS의 /, 크레이콘의 /는 언제나 실수 나눗셈이라 sum / n이 그대로 1.667이에요.
C·C++·Java·C#에서는 두 정수끼리 나누면 정수 나눗셈이라 5 / 3이 1이 되고, 그 값을 double에 넣어도 1.0이에요.
그래서 (double)sum / n(C++ static_cast<double>(sum) / n)처럼 나누기 전에 한쪽을 실수로 바꿔야 해요.
둘째 함정은 합의 크기예요.
10^9짜리 값 1,000개의 합은 10^12라 C·C++·Java·C#의 int(약 21억)를 넘으니 long long·long에 모아요.
Python 정수와 JS·TS·크레이콘의 double은 이 정도면 정확해요.
셋째 함정은 공식이에요.
분산을 "제곱의 평균 - 평균의 제곱"으로 한 번에 구하면, 값이 10^9 가까이이고 흔들림이 작을 때 10^18쯤 되는 두 실수를 빼게 돼요.
double은 유효숫자가 약 16자리라서 그 차이(예: 0.5)가 오차 수백에 묻히고, 음수가 나와 제곱근이 NaN이 되기도 해요.
여덟 언어 모두 같은 double이라 똑같이 무너져요.
평균을 먼저 구하고 (값 - 평균)을 제곱해 더하는 두 번 훑기가 안전해요.
제곱근은 Python math.sqrt, JS·TS Math.sqrt, Java Math.sqrt, C# Math.Sqrt, C sqrt(<math.h>, gcc는 -lm), C++ std::sqrt(<cmath>), 크레이콘 수학.제곱근이에요.
소수 세 자리는 Python f"{x:.3f}", JS·TS toFixed(3), Java String.format("%.3f"), C# ToString("F3"), C·C++ printf("%.3f"), 크레이콘 글자.소수자리맞추기(x, 3)예요.
Java String.format과 C# ToString은 컴퓨터의 지역 설정에 따라 소수점이 쉼표로 나올 수 있어서 Java는 Locale.ROOT, C#은 CultureInfo.InvariantCulture를 넘겨요.

Pythonmain.py · 9 줄
import math s = int(input())for _ in range(s):    n, *xs = map(int, input().split())    mean = sum(xs) / n  # Python 3의 /는 늘 실수 나눗셈이다    # 평균을 먼저 구하고 평균과의 차를 제곱해 더한다(두 번 훑기): 큰 수끼리 빼는 일이 없다    var = sum((x - mean) ** 2 for x in xs) / n    print(f"{mean:.3f} {math.sqrt(var):.3f}")
JavaScriptmain.js · 12 줄
const lines = require("fs").readFileSync(0, "utf8").split("\n");const s = Number(lines[0]);const out = [];for (let i = 1; i <= s; i++) {  const [n, ...xs] = lines[i].trim().split(/\s+/).map(Number);  // JS의 수는 모두 double이라 /는 늘 실수 나눗셈이다(합 10^12도 2^53 안이라 정확하다)  const mean = xs.reduce((a, b) => a + b, 0) / n;  // 평균을 먼저 구하고 평균과의 차를 제곱해 더한다(두 번 훑기): 큰 수끼리 빼는 일이 없다  const variance = xs.reduce((acc, x) => acc + (x - mean) ** 2, 0) / n;  out.push(`${mean.toFixed(3)} ${Math.sqrt(variance).toFixed(3)}`);}console.log(out.join("\n"));
TypeScriptmain.ts · 13 줄
import { readFileSync } from "node:fs";const lines: string[] = readFileSync(0, "utf8").split("\n");const s = Number(lines[0]);const out: string[] = [];for (let i = 1; i <= s; i++) {  const [n, ...xs] = lines[i].trim().split(/\s+/).map(Number);  // JS의 수는 모두 double이라 /는 늘 실수 나눗셈이다(합 10^12도 2^53 안이라 정확하다)  const mean = xs.reduce((a, b) => a + b, 0) / n;  // 평균을 먼저 구하고 평균과의 차를 제곱해 더한다(두 번 훑기): 큰 수끼리 빼는 일이 없다  const variance = xs.reduce((acc, x) => acc + (x - mean) ** 2, 0) / n;  out.push(`${mean.toFixed(3)} ${Math.sqrt(variance).toFixed(3)}`);}console.log(out.join("\n"));
JavaMain.java · 27 줄
import java.io.BufferedReader;import java.io.InputStreamReader;import java.util.Locale; public class Main {    public static void main(String[] args) throws Exception {        BufferedReader in = new BufferedReader(new InputStreamReader(System.in));        int s = Integer.parseInt(in.readLine().trim());        StringBuilder out = new StringBuilder();        for (int k = 0; k < s; k++) {            String[] p = in.readLine().trim().split("\\s+");            int n = Integer.parseInt(p[0]);            long[] xs = new long[n];            long sum = 0; // 10^9 × 1,000 = 10^12: int로는 넘친다            for (int i = 0; i < n; i++) {                xs[i] = Long.parseLong(p[i + 1]);                sum += xs[i];            }            double mean = (double) sum / n; // long / int는 정수 나눗셈이라 한쪽을 double로 바꾼다            // 평균을 먼저 구하고 평균과의 차를 제곱해 더한다(두 번 훑기): 큰 수끼리 빼는 일이 없다            double sq = 0;            for (long x : xs) sq += (x - mean) * (x - mean);            out.append(String.format(Locale.ROOT, "%.3f %.3f", mean, Math.sqrt(sq / n))).append('\n');        }        System.out.print(out);    }}
C#Main.cs · 27 줄
using System;using System.Globalization;using System.Linq;using System.Text; class Program{    static void Main()    {        int s = int.Parse(Console.ReadLine());        var sb = new StringBuilder();        for (int k = 0; k < s; k++)        {            long[] p = Console.ReadLine().Split(' ', StringSplitOptions.RemoveEmptyEntries).Select(long.Parse).ToArray();            int n = (int)p[0];            long[] xs = p.Skip(1).ToArray();            long sum = xs.Sum(); // 10^9 × 1,000 = 10^12: int로는 넘친다            double mean = (double)sum / n; // long / int는 정수 나눗셈이라 한쪽을 double로 바꾼다            // 평균을 먼저 구하고 평균과의 차를 제곱해 더한다(두 번 훑기): 큰 수끼리 빼는 일이 없다            double sq = xs.Sum(x => (x - mean) * (x - mean));            double sd = Math.Sqrt(sq / n);            sb.Append(mean.ToString("F3", CultureInfo.InvariantCulture)).Append(' ')              .Append(sd.ToString("F3", CultureInfo.InvariantCulture)).Append('\n');        }        Console.Write(sb);    }}
C++main.cpp · 24 줄
#include <cmath>#include <cstdio>#include <iostream>#include <vector> int main() {    int s;    std::cin >> s;    for (int k = 0; k < s; k++) {        int n;        std::cin >> n;        std::vector<long long> xs(n);        long long sum = 0; // 10^9 × 1,000 = 10^12: int로는 넘친다        for (auto& x : xs) {            std::cin >> x;            sum += x;        }        double mean = static_cast<double>(sum) / n; // 한쪽을 double로 바꿔야 실수 나눗셈이 된다        // 평균을 먼저 구하고 평균과의 차를 제곱해 더한다(두 번 훑기): 큰 수끼리 빼는 일이 없다        double sq = 0;        for (long long x : xs) sq += (x - mean) * (x - mean);        std::printf("%.3f %.3f\n", mean, std::sqrt(sq / n));    }}
Cmain.c · 26 줄
#include <math.h>#include <stdio.h> int main(void) {    int s;    scanf("%d", &s);    static long long xs[1000];    for (int k = 0; k < s; k++) {        int n;        scanf("%d", &n);        long long sum = 0; /* 10^9 × 1,000 = 10^12: int(약 21억)로는 넘친다 */        for (int i = 0; i < n; i++) {            scanf("%lld", &xs[i]);            sum += xs[i];        }        double mean = (double)sum / n; /* 한쪽을 double로 바꿔야 실수 나눗셈이 된다 */        /* 평균을 먼저 구하고 평균과의 차를 제곱해 더한다(두 번 훑기): 큰 수끼리 빼는 일이 없다 */        double sq = 0;        for (int i = 0; i < n; i++) {            double d = xs[i] - mean;            sq += d * d;        }        printf("%.3f %.3f\n", mean, sqrt(sq / n));    }    return 0;}
Craykonmain.cray · 20 줄
s = 콘솔.줄읽기().수로변환();반복 (k = 0; k < s; k++){    xs = 콘솔.수들읽기();    n = xs[0]; // 맨 앞은 개수    sum = 0;    반복 (i = 1; i <= n; i++)    {        sum += xs[i];    }    // 크레이콘의 /는 늘 실수 나눗셈이다(정수 몫은 수학.몫)    mean = sum / n;    // 평균을 먼저 구하고 평균과의 차를 제곱해 더한다(두 번 훑기): 큰 수끼리 빼는 일이 없다    sq = 0;    반복 (i = 1; i <= n; i++)    {        sq += (xs[i] - mean) * (xs[i] - mean);    }    콘솔.쓰기(글자.소수자리맞추기(mean, 3) + " " + 글자.소수자리맞추기(수학.제곱근(sq / n), 3));}