基于Welford法的C++标准差计算程序异常:均值与结果排查
Fixing Your Test Score Calculator (Welford's Method Included)
Let's break down exactly what's going wrong in your code, plus implement the required Welford's online algorithm correctly—no arrays needed, just a single pass through the data.
1. Input Logic is Broken (Biggest Culprit!)
Right now, your code is skipping the first score and counting the -1 end marker as a valid grade:
- You read the first score into
grade, then immediately read another score at the start of the loop. That first score never gets added to your sum! - When you type
-1to end input, the code still adds it tosumandsumSq, and incrementsnumtests. That's why your average is way off.
2. Standard Deviation Formula is Wrong
Your current calculation has two critical math errors:
- Misplaced parentheses: The correct sample variance formula is
(sumSq - n*avg²)/(n-1), but your code divides only then*avg²term byn-1—totally messing up the calculation. - Missing square root: Standard deviation is the square root of variance, but you never call
sqrt()to get the final value! - No edge case handling: If you enter 0 or 1 scores, you'll hit a divide-by-zero error.
3. You're Not Using Welford's Method
Your code uses the sum/sum-of-squares approach, but the assignment specifically asks for Welford's online algorithm. This method is better for numerical stability (especially with large datasets) and works in a single pass without storing all data. Here's the core logic:
- Track the current mean and a running variance accumulator (
M2) instead of raw sums. - For each new score, update the mean incrementally, then adjust
M2using the difference between the new score and the old/new mean.
Fixed Code (Welford's Method + Correct Input Handling)
#include <iostream> #include <cmath> using namespace std; int main() { double grade = 0; int numtests = 0; double mean = 0.0; double M2 = 0.0; // Welford's variance accumulator cout << "Enter the test scores. Once finished, enter -1 to end input.\n"; // Only process scores that aren't the end marker (-1) while (cin >> grade && grade != -1) { numtests++; double delta = grade - mean; mean += delta / numtests; double delta2 = grade - mean; M2 += delta * delta2; } cout << "The number of scores: " << numtests << "\n"; // Handle edge cases to avoid crashes/invalid results if (numtests == 0) { cout << "No test scores were entered.\n"; return 0; } double average = mean; double std_dev; if (numtests == 1) { std_dev = 0.0; // A single score has no meaningful sample standard deviation } else { std_dev = sqrt(M2 / (numtests - 1)); } cout << "Average: " << average << "\n"; cout << "Std. Deviation: " << std_dev << "\n"; return 0; }
What Changed?
- Input Fix: The loop now checks if the input is
-1before processing the score, so the end marker is ignored, and the first score gets counted properly. - Welford's Implementation: We track
meanandM2iteratively—no sums, no arrays, just a single pass through the input as required. - Edge Cases: We handle empty input and single-score input to avoid crashes and invalid calculations.
内容的提问来源于stack exchange,提问作者Larry the Guy
相关产品推荐
相关产品推荐

