예제 #1
0
        // Start from definition C_r = < (x-M1)^r > and use the binomial formula to write (x - M1)^r in terms of powers of x and M1.
        //   C_r = \sum_{k=0}^{n} {n \choose k} (-\mu)^k M_{r-k}

        internal static double RawToCentral(double[] M, int r)
        {
            Debug.Assert(M.Length > 1);
            Debug.Assert(0 <= r && r < M.Length);
            Debug.Assert(M[0] == 1.0);

            double mmu             = -M[1];
            IEnumerator <double> B = AdvancedIntegerMath.BinomialCoefficients(r).GetEnumerator();

            // k = 0 term is M_r
            B.MoveNext();
            int    i    = r;   // tracks r - k
            double mmuk = 1.0; // tracks (-\mu)^k
            double C    = M[r];

            while (B.MoveNext())
            {
                i--;
                mmuk *= mmu;
                C    += mmuk * B.Current * M[i];
            }

            // final term is (-\mu)^r {r \choose r} M_0 = (-\mu)^r; this will be wrong if M[0] != 1

            return(C);
        }
예제 #2
0
        // M_r = \sum_{k=0}^{r} {r \choose k} \mu^k C_{r-k}

        internal static double CentralToRaw(double mu, double[] C, int r)
        {
            Debug.Assert(C.Length > 0);
            Debug.Assert(0 <= r && r < C.Length);
            Debug.Assert(C[0] == 1.0);
            //Debug.Assert(C[1] == 0.0);

            IEnumerator <double> B = AdvancedIntegerMath.BinomialCoefficients(r).GetEnumerator();

            // k = 0 term
            B.MoveNext();
            int    kp  = r;   // tracks k' = k - r
            double muk = 1.0; // tracks \mu^k
            double M   = C[r];

            while (B.MoveNext())
            {
                kp--;
                muk *= mu;
                M   += B.Current * muk * C[kp];
            }

            // final term is \mu^r {r \choose r} C_0 = \mu^r; this will be wrong if C[0] != 1
            // second to final term is 0; this will be wrong if C[1] != 0

            return(M);
        }
예제 #3
0
 public void BinomialCoefficientAgreement()
 {
     foreach (int n in TestUtilities.GenerateIntegerValues(1, 100, 4))
     {
         int         m = -1;
         IEnumerator B = AdvancedIntegerMath.BinomialCoefficients(n).GetEnumerator();
         while (B.MoveNext())
         {
             m++;
             Assert.IsTrue(TestUtilities.IsNearlyEqual((double)B.Current, AdvancedIntegerMath.BinomialCoefficient(n, m)));
         }
     }
 }
        // Privault, "Generalized Bell polynomials and the combinatorics of Poisson central moments",
        // The Electronic Jounrnal of Combinatorics 2011
        // (http://www.ntu.edu.sg/home/nprivault/papers/central_moments.pdf),
        // derives the recurrence
        //   C_{r+1} = \mu \sum{s=0}^{r-1} { r \choose s } C_s
        // for central moments of the Poisson distribution, directly from the definition.

        // For the record, here is the derivation, which I have not found elsewhere. By
        // Poisson PMF, mean, and definition of central moment:
        //   C_{r+1} = e^{-\mu} \sum_{k=0}^{\infty} \frac{\mu^k}{k!} (k - \mu)^{r+1}
        // Expand one factor of (k - \mu) to get
        //   C_{r+1} = e^{-\mu} \sum_{k=1}^{\infty} \frac{\mu^k (k - \mu)^{r}}{(k-1)!}
        //            -e^{-\mu} \sum_{k=0}^{\infty} \frac{\mu^{k+1} (k - \mu)^{r}}{k!}
        // Note k=0 does not contribute to first term because of multiplication by k.
        // Now in first term, redefine dummy summation variable k -> k + 1 to get
        //   C_{r+1} = e^{-\mu} \sum_{k=0}^{\infty} \frac{\mu^{k+1}}{k!}
        //             \left[ (k + 1 - \mu)^{r} - (k - \mu)^{r} \right]
        // Now use the binomial theorem to expand (k - \mu + 1) in factors of (k -\mu) and 1.
        //   C_{r+1} = e^{-\mu} \sum_{k=0}^{\infty} \frac{\mu^{k+1}}{k!}
        //             \sum_{s=0}^{r-1} { r \choose s } (k - \mu)^{s}
        // The s=r term is canceled by the subtracted (k - \mu)^{r}, so the binomial sum only goes to s=r-1.
        // Switch the order of the sums and notice \sum_{k} \frac{\mu^{k}}{k!} (k - \mu)^{s} = C_{s}
        // to obtain the result. Wow.

        // This is almost, but not quite, the same recurrence as for the raw moments.
        // For the raw moment recursion, the bottom binomial argument runs over its full range.
        // For the central moment recursion, the final value of the bottom binomial argument is left out.
        // Also, for raw moments start the recursion with M_0 = 1, M_1 = \mu. For central moments,
        // start the recursion with C_0 = 1, C_1 = 0.

        private void ComputePoissonCentralMoments(double[] C)
        {
            for (int r = 2; r < C.Length; r++)
            {
                IEnumerator <double> binomial = AdvancedIntegerMath.BinomialCoefficients(r - 1).GetEnumerator();
                double t = 0.0;
                for (int s = 0; s < r - 1; s++)
                {
                    binomial.MoveNext();
                    t += binomial.Current * C[s];
                }
                C[r] = mu * t;
            }
        }
예제 #5
0
        public void BellNumberRecurrence()
        {
            // B_{n+1} = \sum_{k=0}^{n} {n \choose k} B_k

            foreach (int r in TestUtilities.GenerateIntegerValues(50, 100, 2))
            {
                double s = 0.0;
                IEnumerator <double> b = AdvancedIntegerMath.BinomialCoefficients(r).GetEnumerator();
                for (int k = 0; k <= r; k++)
                {
                    b.MoveNext();
                    s += b.Current * AdvancedIntegerMath.BellNumber(k);
                }
                Assert.IsTrue(TestUtilities.IsNearlyEqual(s, AdvancedIntegerMath.BellNumber(r + 1)));
            }
        }
예제 #6
0
 public void BinomialCoefficientSums()
 {
     foreach (int n in TestUtilities.GenerateIntegerValues(1, 100, 8))
     {
         double S0 = 0.0;
         double S1 = 0.0;
         double S2 = 0.0;
         int    k  = 0;
         foreach (double B in AdvancedIntegerMath.BinomialCoefficients(n))
         {
             S0 += B;
             S1 += k * B;
             S2 += k * k * B;
             k  += 1;
         }
         Assert.IsTrue(TestUtilities.IsNearlyEqual(S0, MoreMath.Pow(2, n)));
         Assert.IsTrue(TestUtilities.IsNearlyEqual(S1, MoreMath.Pow(2, n - 1) * n));
         Assert.IsTrue(TestUtilities.IsNearlyEqual(S2, MoreMath.Pow(2, n - 2) * n * (n + 1)));
     }
 }
예제 #7
0
        internal override double[] CentralMoments(int rMax)
        {
            double[] C = new double[rMax + 1];
            C[0] = 1.0;
            if (rMax == 0)
            {
                return(C);
            }
            C[1] = 0.0;

            for (int r = 2; r <= rMax; r++)
            {
                double s = 0.0;
                IEnumerator <double> B = AdvancedIntegerMath.BinomialCoefficients(r - 1).GetEnumerator();
                for (int k = 0; k <= r - 2; k++)
                {
                    B.MoveNext();
                    s += B.Current * (n * q * C[k] - C[k + 1]);
                }
                C[r] = p * s;
            }

            return(C);
        }
예제 #8
0
        /// <inheritdoc />
        public override int InverseLeftProbability(double P)
        {
            if ((P < 0.0) || (P > 1.0))
            {
                throw new ArgumentOutOfRangeException(nameof(P));
            }
            if (n < 16)
            {
                // for small m, just add up probabilities directly
                // here PP is p^k q^(m-k), PS is the sum of PPs up to k, and r = p / q is used to increment PP
                double r  = p / q;
                double PP = MoreMath.Pow(q, n);
                double PS = 0.0;

                int k = -1;
                foreach (double B in AdvancedIntegerMath.BinomialCoefficients(n))
                {
                    k++;
                    PS += B * PP;
                    if (PS >= P)
                    {
                        return(k);
                    }
                    PP *= r;
                }
                // we "shouldn't" reach here, but if floating point jitter makes all the values add up to 0.999999 instead of 1,
                // and we have P = 0.99999999, it could happen, so in this case we return m
                return(n);
            }
            else
            {
                double Q = 1.0 - P;
                if (Q == 0.0)
                {
                    return(n);
                }
                double mu = n * p;

                int kmin, kmax;
                if (P < 0.5)
                {
                    kmin = 0;
                    kmax = (int)Math.Ceiling(mu);
                    int kChernov = (int)Math.Floor(mu - Math.Sqrt(-2.0 * mu * Math.Log(P)));
                    if (kChernov > kmin)
                    {
                        kmin = kChernov;
                    }
                }
                else
                {
                    kmin = (int)Math.Floor(mu);
                    kmax = n;
                    int kMarkov = (int)Math.Ceiling(mu / Q);
                    if (kMarkov < kmax)
                    {
                        kmax = kMarkov;
                    }
                    int kCantelli = (int)Math.Ceiling(mu + Math.Sqrt(mu * q * P / Q));
                    if (kCantelli < kmax)
                    {
                        kmax = kCantelli;
                    }
                    int kChernov = (int)Math.Ceiling(mu + Math.Sqrt(-2.0 * mu * Math.Log(Q)));
                    if (kChernov < kmax)
                    {
                        kmax = kChernov;
                    }
                }

                return(InverseLeftProbability(kmin, kmax, P));
            }
        }
예제 #9
0
        internal double[] Cumulants(int rMax)
        {
            double[] K = new double[rMax + 1];

            K[0] = 0.0;
            if (rMax == 0)
            {
                return(K);
            }

            K[1] = this.Mean;
            if (rMax == 1)
            {
                return(K);
            }

            // C. L. Mallows & J. Riordan, "The Inversion Enumerator for Labeled Trees" derives a
            // recurrence for lognormal cumulants.
            //   K_r = (M_1)^r (e^x - 1)^{r-1} J_{r-1}(x)
            // Here x = e^{\sigma^2} and J_{n}(x) is a polynomial with all positive coefficients.
            //   J_{n+1} = \sum_{k=0}^{n} {n \choose k} (1 + x + \cdots x^k) J_{k} J_{n - k}
            // with J_0 = J_1 = 1.

            // Thus the first few cumulants are
            //   K_0 = 1
            //   K_1 = M_1
            //   K_2 = (M_1)^2 (x - 1)
            //   K_3 = (M_1)^3 (x - 1)^2 (2 + x)
            //   K_4 = (M_1)^4 (x - 1)^3 (6 + 6 x^2 + 3 x^2 + x^3)
            //   K_5 = (M_1)^5 (x - 1)^4 (24 + 36 x + 30 x^2 + 20 x^3 + 4 x^4 + x^5)

            double x = Math.Exp(sigma * sigma);

            // Form L_k = 1 + x + \cdots + x^{k}
            double[] L  = new double[rMax];
            double   xk = 1.0;

            L[0] = 1.0;
            for (int i = 1; i < L.Length; i++)
            {
                xk  *= x;
                L[i] = L[i - 1] + xk;
            }

            double y  = MoreMath.ExpMinusOne(sigma * sigma) * K[1];
            double yk = K[1];

            double[] J = new double[rMax];
            J[0] = 1.0;
            for (int i = 1; i < rMax; i++)
            {
                J[i] = 0.0;
                IEnumerator <double> B = AdvancedIntegerMath.BinomialCoefficients(i - 1).GetEnumerator();
                for (int j = 0; j < i; j++)
                {
                    B.MoveNext();
                    J[i] += B.Current * L[j] * J[j] * J[(i - 1) - j];
                }
                yk      *= y;
                K[i + 1] = yk * J[i];
            }

            return(K);
        }
예제 #10
0
        /// <inheritdoc />
        public override int InverseLeftProbability(double P)
        {
            if ((P < 0.0) || (P > 1.0))
            {
                throw new ArgumentOutOfRangeException("P");
            }
            if (n < 16)
            {
                // for small m, just add up probabilities directly
                // here PP is p^k q^(m-k), PS is the sum of PPs up to k, and r = p / q is used to increment PP
                double r  = p / q;
                double PP = MoreMath.Pow(q, n);
                double PS = 0.0;

                /*
                 * using (IEnumerator<double> B = AdvancedIntegerMath.BinomialCoefficients(m).GetEnumerator()) {
                 *  // by ending this loop at k = m - 1 instead of k = m, we not only avoid evaluating P(m), but we also
                 *  // ensure that if PS = 0.999999 due to floating point noise, and we have P = 0.99999999, we dont
                 *  // return m+1 when we should return m
                 *  for (int k = 0; k < m; k++) {
                 *      B.MoveNext();
                 *      PS += B.Current * PP;
                 *      if (PS >= P) return(k);
                 *      PP *= r;
                 *  }
                 *  return (m);
                 * }
                 */

                int k = -1;
                foreach (double B in AdvancedIntegerMath.BinomialCoefficients(n))
                {
                    k++;
                    PS += B * PP;
                    if (PS >= P)
                    {
                        return(k);
                    }
                    PP *= r;
                }
                // we "shouldn't" reach here, but if floating point jitter makes all the values add up to 0.999999 instead of 1,
                // and we have P = 0.99999999, it could happen, so in this case we return m
                return(n);

                /*
                 * // for small distributions, just add probabilities directly
                 * int k = 0;
                 * double P0 = MoreMath.Pow(q, m);
                 * double PP = P0;
                 * while (k < m) {
                 *  if (P <= PP) break;
                 *  k++;
                 *  P0 *= (m + 1 - k) / k * p / q;
                 *  PP += P0;
                 * }
                 * return (k);
                 */
            }
            else
            {
                // for larger distributions, use bisection
                // this will require log_{2}(m) CDF evaluations, which is at most 31
                int ka = 0;
                int kb = n;
                while (ka != kb)
                {
                    int k = (ka + kb) / 2;
                    if (P > LeftInclusiveProbability(k))
                    {
                        ka = k + 1;
                    }
                    else
                    {
                        kb = k;
                    }
                }
                return(ka);
            }
        }