题目链接

统计监控数据

题目描述

给定一个由若干个监控样本首尾相接组成的浮点数序列。每个样本都严格包含 19 个浮点特征。任务是解析整个序列,并对 19 个特征中的每一列分别计算 8 个指定的统计量。

需要计算的统计量及其顺序:

  1. mean: 算术平均值
  2. max: 最大值
  3. min: 最小值
  4. ptp: 极差 (max - min)
  5. std: 总体标准差
  6. var: 总体方差
  7. skew: 总体偏度
  8. kurt: 总体超峰度

公式说明:

  • 为样本数。
  • var =
  • std =
  • skew = 。若 std 为 0,则 skew 为 0。
  • kurt = 。若 std 为 0,则 kurt 为 0。

输入:

  • 一行空格分隔的浮点数,总数是 19 的整数倍。

输出:

  • 一行空格分隔的浮点数,共 个。
  • 先输出第 0 列特征的 8 个统计量,然后是第 1 列,以此类推,直到第 18 列。
  • 所有数值均保留两位小数。

解题思路

这是一个纯粹的数据处理和统计计算问题。核心在于正确地组织数据并实现题目要求的各个统计公式。

  1. 数据读取与重组

    • 首先,一次性读取输入行中的所有浮点数,存入一个一维数组(或列表) all_data 中。
    • 计算样本数量
    • 为了便于按列计算,我们可以将一维的 all_data 重组为一个 的二维矩阵 datadata[i][j] 表示第 个样本的第 个特征值。
  2. 按列进行统计计算

    • 外层循环遍历 19 个特征列(col from 0 to 18)。
    • 对于每一列,执行以下计算:
      • 第一次遍历 (Pass 1):遍历该列的所有 个数据点。在这次遍历中,我们可以同时计算出 sum, max, 和 min
      • 计算基础统计量:
        • mean = sum / N
        • ptp = max - min
      • 第二次遍历 (Pass 2):再次遍历该列的 个数据点,利用已经计算出的 mean 来计算方差所需的平方和 sum_sq_diff
        • sum_sq_diff += (value - mean) * (value - mean)
      • 计算方差和标准差:
        • var = sum_sq_diff / N
        • std = sqrt(var)
      • 处理标准差为 0 的特殊情况:
        • 如果 std 约等于 0(需要考虑浮点数精度),则根据题目要求,skewkurt 都直接设为 0。
      • 第三次遍历 (Pass 3):如果 std 不为 0,则需要第三次遍历来计算偏度和峰度。
        • 对于每个值,计算其标准化值
        • 累加 到各自的总和 sum_cubedsum_quartic 中。
      • 计算偏度和峰度:
        • skew = sum_cubed / N
        • kurt = sum_quartic / N - 3
  3. 格式化输出

    • 将每一列计算出的 8 个统计量按顺序存储起来。
    • 所有计算完成后,遍历存储结果的列表,将每个数值格式化为保留两位小数的字符串,并用空格连接后一次性输出。

代码

#include <iostream>
#include <vector>
#include <string>
#include <sstream>
#include <cmath>
#include <iomanip>
#include <numeric>
#include <limits>

using namespace std;

int main() {
    ios::sync_with_stdio(false);
    cin.tie(NULL);

    string line;
    getline(cin, line);
    stringstream ss(line);
    double num;
    vector<double> all_data;
    while (ss >> num) {
        all_data.push_back(num);
    }

    int n_features = 19;
    int n_samples = all_data.size() / n_features;

    vector<vector<double>> data(n_samples, vector<double>(n_features));
    for (int i = 0; i < all_data.size(); ++i) {
        data[i / n_features][i % n_features] = all_data[i];
    }

    cout << fixed << setprecision(2);

    for (int j = 0; j < n_features; ++j) {
        vector<double> col_data(n_samples);
        for (int i = 0; i < n_samples; ++i) {
            col_data[i] = data[i][j];
        }

        // 第一次遍历:计算总和、最小值、最大值
        double sum = 0.0;
        double max_val = -numeric_limits<double>::infinity();
        double min_val = numeric_limits<double>::infinity();
        for (double val : col_data) {
            sum += val;
            if (val > max_val) max_val = val;
            if (val < min_val) min_val = val;
        }

        double mean = sum / n_samples;
        double ptp = max_val - min_val;

        // 第二次遍历:计算方差
        double sum_sq_diff = 0.0;
        for (double val : col_data) {
            sum_sq_diff += (val - mean) * (val - mean);
        }
        double var = sum_sq_diff / n_samples;
        double std = sqrt(var);

        double skew = 0.0;
        double kurt = 0.0;

        // 第三次遍历:计算偏度和峰度
        if (std > 1e-9) {
            double sum_cubed = 0.0;
            double sum_quartic = 0.0;
            for (double val : col_data) {
                double z = (val - mean) / std;
                sum_cubed += z * z * z;
                sum_quartic += z * z * z * z;
            }
            skew = sum_cubed / n_samples;
            kurt = sum_quartic / n_samples - 3.0;
        }
        
        cout << mean << " " << max_val << " " << min_val << " " << ptp << " "
             << std << " " << var << " " << skew << " " << kurt 
             << (j == n_features - 1 ? "" : " ");
    }
    cout << endl;

    return 0;
}

import java.util.Scanner;
import java.util.ArrayList;
import java.util.List;
import java.lang.Math;

public class Main {
    public static void main(String[] args) {
        Scanner sc = new Scanner(System.in);
        List<Double> allData = new ArrayList<>();
        while (sc.hasNextDouble()) {
            allData.add(sc.nextDouble());
        }

        int nFeatures = 19;
        int nSamples = allData.size() / nFeatures;

        double[][] data = new double[nSamples][nFeatures];
        for (int i = 0; i < allData.size(); i++) {
            data[i / nFeatures][i % nFeatures] = allData.get(i);
        }

        StringBuilder sb = new StringBuilder();

        for (int j = 0; j < nFeatures; j++) {
            double[] colData = new double[nSamples];
            for (int i = 0; i < nSamples; i++) {
                colData[i] = data[i][j];
            }

            // 第一次遍历:计算总和、最小值、最大值
            double sum = 0.0;
            double maxVal = -Double.MAX_VALUE;
            double minVal = Double.MAX_VALUE;
            for (double val : colData) {
                sum += val;
                if (val > maxVal) maxVal = val;
                if (val < minVal) minVal = val;
            }

            double mean = sum / nSamples;
            double ptp = maxVal - minVal;

            // 第二次遍历:计算方差
            double sumSqDiff = 0.0;
            for (double val : colData) {
                sumSqDiff += (val - mean) * (val - mean);
            }
            double var = sumSqDiff / nSamples;
            double std = Math.sqrt(var);

            double skew = 0.0;
            double kurt = 0.0;

            // 第三次遍历:计算偏度和峰度
            if (std > 1e-9) {
                double sumCubed = 0.0;
                double sumQuartic = 0.0;
                for (double val : colData) {
                    double z = (val - mean) / std;
                    sumCubed += z * z * z;
                    sumQuartic += z * z * z * z;
                }
                skew = sumCubed / nSamples;
                kurt = sumQuartic / nSamples - 3.0;
            }
            
            sb.append(String.format("%.2f %.2f %.2f %.2f %.2f %.2f %.2f %.2f",
                mean, maxVal, minVal, ptp, std, var, skew, kurt));
            
            if (j < nFeatures - 1) {
                sb.append(" ");
            }
        }
        System.out.println(sb.toString());
    }
}

import math

def main():
    all_data = list(map(float, input().split()))

    n_features = 19
    n_samples = len(all_data) // n_features

    results = []
    for j in range(n_features):
        col_data = [all_data[i * n_features + j] for i in range(n_samples)]
        
        # 第一次遍历:计算总和、最小值、最大值
        sum_val = sum(col_data)
        max_val = -float('inf')
        min_val = float('inf')
        for val in col_data:
            if val > max_val: max_val = val
            if val < min_val: min_val = val

        mean = sum_val / n_samples
        ptp = max_val - min_val

        # 第二次遍历:计算方差
        sum_sq_diff = sum([(val - mean) ** 2 for val in col_data])
        var = sum_sq_diff / n_samples
        std = math.sqrt(var)

        skew = 0.0
        kurt = 0.0

        # 第三次遍历:计算偏度和峰度
        if std > 1e-9:
            sum_cubed = sum([((val - mean) / std) ** 3 for val in col_data])
            sum_quartic = sum([((val - mean) / std) ** 4 for val in col_data])
            skew = sum_cubed / n_samples
            kurt = sum_quartic / n_samples - 3.0
            
        results.extend([mean, max_val, min_val, ptp, std, var, skew, kurt])

    print(" ".join([f"{x:.2f}" for x in results]))

if __name__ == "__main__":
    main()

算法及复杂度

  • 算法:数据重组与统计计算
  • 时间复杂度:,其中 是输入的浮点数总数。我们需要读取所有数据,然后对每个特征列(共 19 列)进行常数次遍历(3 次),总计算量与数据点总数成正比。
  • 空间复杂度:,需要存储所有读入的浮点数以便进行多次计算。