/// <summary> /// Computes the unnormalized cumulative distribution function and other attributes for the /// distribution (like mean, variance, and so on). /// </summary> /// <remarks> /// Also remember to change <see cref="SetUp(int, IEnumerable{double}, out double[])"/> /// when changing this method. /// </remarks> private void UpdateHelpers() { var weightsSum = _weights.Sum(); // It will store the sum of all UNNORMALIZED weights. var cdf = new double[_weights.Count]; // It will store NORMALIZED cdf. var tmpMean = 0.0; var maxW = 0.0; // It will store max weight (all weights are positive). var maxI = 0; // It will store max weight index. // Let's normalize all weights, if necessary. if (!TMath.AreEqual(weightsSum, 1.0)) { for (var i = 0; i < _weights.Count; ++i) { _weights[i] /= weightsSum; } Debug.Assert(TMath.AreEqual(_weights.Sum(), 1.0)); } weightsSum = 0.0; // Reset weight sum to use it for cdf. // One big loop to compute all helpers needed by this distribution. for (var i = 0; i < _weights.Count; ++i) { var w = _weights[i]; weightsSum += w; cdf[i] = weightsSum; tmpMean += w * (i + 1.0); // Plus one because it is zero-based. if (w > maxW) { maxW = w; maxI = i; } } // Finalize some results... _cdf = cdf; Mean = tmpMean - 1.0; // Minus one to make it zero-based. Mode = new double[] { maxI }; var halfWeightsSum = weightsSum / 2.0; var tmpMedian = double.NaN; var tmpVar = 0.0; // We still need another loop to compute variance; as for the mean, the plus/minus one is needed. for (var i = 0; i < _weights.Count; ++i) { if (double.IsNaN(tmpMedian) && _cdf[i] >= halfWeightsSum) { tmpMedian = i; } tmpVar += _weights[i] * TMath.Square(i + 1 - Mean); } // Finalize last results... Median = tmpMedian; Variance = tmpVar - 1.0; }