Skip to content

feat: add WelfordAlgorithm, online mean and variance in one pass - #7602

Open
alxkm wants to merge 1 commit into
TheAlgorithms:masterfrom
alxkm:feat/welford-algorithm
Open

feat: add WelfordAlgorithm, online mean and variance in one pass#7602
alxkm wants to merge 1 commit into
TheAlgorithms:masterfrom
alxkm:feat/welford-algorithm

Conversation

@alxkm

@alxkm alxkm commented Sep 10, 2026

Copy link
Copy Markdown
Member

Adds Welford's algorithm: the mean and the variance of a stream in a single pass, in O(1) time per sample and O(1) memory however long the stream is.

The textbook formula Var = (sum(x^2) - n * mean^2) / (n - 1) is fast but numerically treacherous -- sum(x^2) and n * mean^2 can be huge and nearly equal, so their difference loses most of its significant digits and may even come out negative. Welford's recurrence never forms those intermediate values; it carries the running mean and M2, the sum of squared deviations from that running mean:

n     <- n + 1
delta <- x - mean
mean  <- mean + delta / n
M2    <- M2 + delta * (x - mean)   // the second factor uses the updated mean

Two things come with the accumulator:

  • remove(double) runs the recurrence backwards, which turns it into the statistics of a sliding window: hand the incoming sample to add and the sample that just left the window to remove. The Javadoc is explicit that this is the one operation that can degrade over a very long run, since the removed value no longer matches the mean it was added to, and that the accumulator should be recreated periodically when that matters.
  • merge(WelfordAlgorithm, WelfordAlgorithm) is Chan's parallel update, so partial summaries computed on different shards combine exactly rather than approximately.

The class exposes both the sample and the population variance and standard deviation, plus the standard error, sum and sumOfSquaredDeviations, and it refuses non-finite samples rather than silently poisoning the state with NaN.

WelfordAlgorithmTest covers 20 cases. Among them: the results are compared against a two-pass computation for several sample counts, a stream offset by 1e9 stays accurate where the naive sum of squares collapses, remove exactly undoes add, a sliding window is checked against recomputation from scratch, removing a sample far from the mean cannot push the variance below zero, merge is checked against the same data accumulated in one go and with empty operands, and an empty accumulator reports NaN instead of dividing by zero.

Checklist

  • I have read CONTRIBUTING.md.
  • This pull request is all my own work -- I have not plagiarized it.
  • All filenames are in PascalCase.
  • All functions and variable names follow Java naming conventions.
  • All new algorithms have a URL in their comments that points to Wikipedia or other similar explanations.
  • All new algorithms include a corresponding test class that validates their functionality.
  • All new code is formatted with clang-format -i --style=file path/to/your/file.java

Welford's recurrence keeps a running mean and the sum of squared deviations from it, so it never forms the large nearly equal intermediate values that make the textbook variance formula lose its significant digits, and it needs O(1) time per sample and O(1) memory regardless of the stream length.

Beyond the plain accumulation it supports removal, which runs the recurrence backwards and turns the accumulator into the statistics of a sliding window, and a static merge implementing Chan's parallel update so partial results from different shards combine exactly.

Signed-off-by: alxkm <19151554+alxkm@users.noreply.github.com>
@codecov-commenter

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 81.03%. Comparing base (1f08afb) to head (9403688).

Additional details and impacted files
@@             Coverage Diff              @@
##             master    #7602      +/-   ##
============================================
+ Coverage     81.01%   81.03%   +0.02%     
- Complexity     7740     7768      +28     
============================================
  Files           825      826       +1     
  Lines         24594    24645      +51     
  Branches       4810     4814       +4     
============================================
+ Hits          19925    19972      +47     
- Misses         3906     3907       +1     
- Partials        763      766       +3     

☔ View full report in Codecov by Harness.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants