// Faa di Bruno's formula expresses raw moments in terms of cumulants. // M_r = \sum_{m_1 + \cdots + m_k = r} \frac{r!}{m_1 \cdots m_k} K_1 \cdots K_k // That is: take all partitions of r. (E.g. for r = 4, thre are 5 partitions: 1 + 1 + 1 + 1 = 4, 1 + 1 + 2 = 4, 2 + 2 = 4, 1 + 3 = 4, and 4 = 4). // Each partition will contribute one term. Each appearance of an integer k in the partition will contribute one factor of the kth cumulant // to that term. (E.g. K_1^4, K_1^2 K_2, K_2^2, K_1 K_3, and K_4.) // Each term has a combinatoric factor of r! divided by the product of all the integers in the partition. (E.g. 1^4, 1^2 * 2, 2^2, 1 * 3, and 4.) internal static double CumulantToMoment(double[] K, int r, bool central) { Debug.Assert(K != null); Debug.Assert(r > 0); Debug.Assert(K.Length >= r); double M = 0.0; foreach (int[] partition in AdvancedIntegerMath.InternalPartitions(r)) { double dM = AdvancedIntegerMath.Factorial(r); int u = 0; // tracks the last observed partition member int m = 1; // tracks the multiplicity of the current partition member foreach (int v in partition) { // if we are computing central moments, ignore all terms with a factor of K_1 if (central && (v == 1)) { dM = 0.0; break; } if (v == u) { m++; } else { m = 1; } dM *= K[v] / AdvancedIntegerMath.Factorial(v) / m; u = v; } M += dM; } return(M); // This method of reading out a partition is a bit klunky. We really want to know the multiplicity of each element, // but that isn't straightforwardly available. In fact, this method will break if identical elements are not adjacent // in the array (e.g. 4 = 1 + 2 + 1 + 1). Would be nice to address this, perhaps by actually returning a multiplicity // representation of parititions. }