【问题标题】:How do I transform an Inverse CDF coded for a standard deviation of one to a different STDDEV?如何将针对标准偏差编码为 1 的逆 CDF 转换为不同的 STDDEV?
【发布时间】:2012-09-28 21:21:27
【问题描述】:

我正在使用逆累积分布函数 (CDF) 作为数值积分算法的一部分进行拒绝抽样。

我发现了两种可能的实现方式:

  1. 在 C 和 Java 中(以及其他,但不是 C#,所以我需要翻译):

http://home.online.no/~pjacklam/notes/invnorm/

  1. 在 C# 中,有 StatisticFormula.InverseNormalDistribution(在 System.Windows.Forms.DataVisualization.Charting 中)。鉴于微软多年前在 Excel 中执行不良 NORMINV 的记录,我对此持怀疑态度。

这两个函数都假定均值为零(我将使用该函数),标准差为 1。如何转换此函数的输入和/或输出,以使标准差与 1 不同?

我知道高斯分布:

f(x,mean,sd) = (1/(sd*sqrt(2*pi)))exp(-0.5((x-mean)/sd)^2)

所以如果我有 g(x) = f(x,0,1),那么 f(x,mean,sd) = (1/sd)*g(x/sd)。

将高斯转换为不同的标准差很容易。 我可以为逆 CDF 做类似的事情吗?

【问题讨论】:

    标签: c# probability standard-deviation


    【解决方案1】:

    如果一切都失败了,请尝试 MS Excel。我使用 NORMINV(它允许您提供标准偏差)计算了逆 CDF,并将其与使用 NORMSINV(假设标准偏差为 1)返回的值进行了比较。令人高兴的结果是,您可以将使用 1 的标准偏差获得的结果乘以所需的标准偏差。

    NORMSINV(x) * SD = NORMINV(x,0,SD)

    顺便说一句:我尝试使用 Microsoft 库,但没有成功。您只能在图表的上下文中调用函数,这是不可接受的开销。我最终将 Jeremy Lea 的 C 实现移植到 C#。引用了我在问题中给出的链接http://home.online.no/~pjacklam/notes/invnorm/

    我在移植时犯了一个错误,或者 C 版本有错误,因为我在负输入时得到了很好的准确度,但是当 x > 6 时准确度迅速下降,并且 x = 7。

    【讨论】:

    • 链接已损坏。您还有其他参考资料吗?
    • 抱歉 - 我为前雇主编写了 C# 版本,不再拥有源代码。
    • 不用担心。我在网上找到了一个实现。我将其添加为答案。
    【解决方案2】:

    由于 Paul Chernoch 的答案与实现的链接断开,我不得不寻找自己的答案。我在这里找到了 R 版本的逆 CDF 的实现,它具有参数化的均值和标准差:Standard Normal Distribution z-value function in C#

    为了防止另一个断开链接的风险,我在下面列出了我的方法副本。

            /// <summary>
        /// Quantile function (Inverse CDF) for the normal distribution.
        /// </summary>
        /// <param name="p">Probability.</param>
        /// <param name="mu">Mean of normal distribution.</param>
        /// <param name="sigma">Standard deviation of normal distribution.</param>
        /// <param name="isLowerTail">If true, probability is P[X <= x], otherwise P[X > x].</param>
        /// <param name="isLogValues">If true, probabilities are given as log(p).</param>
        /// <returns>P[X <= x] where x ~ N(mu,sigma^2)</returns>
        /// <remarks>See https://svn.r-project.org/R/trunk/src/nmath/qnorm.c </remarks>
        public static double QNorm(double p, double mu, double sigma, bool isLowerTail, bool isLogValues)
        {
            if (double.IsNaN(p) || double.IsNaN(mu) || double.IsNaN(sigma)) return (p + mu + sigma);
            double ans;
            bool isBoundaryCase = R_Q_P01_boundaries(p, double.NegativeInfinity, double.PositiveInfinity, isLowerTail, isLogValues, out ans);
            if (isBoundaryCase) return (ans);
            if (sigma < 0) return (double.NaN);
            if (sigma == 0) return (mu);
    
            double p_ = R_DT_qIv(p, isLowerTail, isLogValues);
            double q = p_ - 0.5;
            double r, val;
    
            if (Math.Abs(q) <= 0.425)  // 0.075 <= p <= 0.925
            {
                r = .180625 - q * q;
                val = q * (((((((r * 2509.0809287301226727 +
                           33430.575583588128105) * r + 67265.770927008700853) * r +
                         45921.953931549871457) * r + 13731.693765509461125) * r +
                       1971.5909503065514427) * r + 133.14166789178437745) * r +
                     3.387132872796366608)
                / (((((((r * 5226.495278852854561 +
                         28729.085735721942674) * r + 39307.89580009271061) * r +
                       21213.794301586595867) * r + 5394.1960214247511077) * r +
                     687.1870074920579083) * r + 42.313330701600911252) * r + 1.0);
            }
            else
            {
                r = q > 0 ? R_DT_CIv(p, isLowerTail, isLogValues) : p_;
                r = Math.Sqrt(-((isLogValues && ((isLowerTail && q <= 0) || (!isLowerTail && q > 0))) ? p : Math.Log(r)));
    
                if (r <= 5)              // <==> min(p,1-p) >= exp(-25) ~= 1.3888e-11
                {
                    r -= 1.6;
                    val = (((((((r * 7.7454501427834140764e-4 +
                            .0227238449892691845833) * r + .24178072517745061177) *
                          r + 1.27045825245236838258) * r +
                         3.64784832476320460504) * r + 5.7694972214606914055) *
                       r + 4.6303378461565452959) * r +
                      1.42343711074968357734)
                     / (((((((r *
                              1.05075007164441684324e-9 + 5.475938084995344946e-4) *
                             r + .0151986665636164571966) * r +
                            .14810397642748007459) * r + .68976733498510000455) *
                          r + 1.6763848301838038494) * r +
                         2.05319162663775882187) * r + 1.0);
                }
                else                     // very close to  0 or 1 
                {
                    r -= 5.0;
                    val = (((((((r * 2.01033439929228813265e-7 +
                            2.71155556874348757815e-5) * r +
                           .0012426609473880784386) * r + .026532189526576123093) *
                         r + .29656057182850489123) * r +
                        1.7848265399172913358) * r + 5.4637849111641143699) *
                      r + 6.6579046435011037772)
                     / (((((((r *
                              2.04426310338993978564e-15 + 1.4215117583164458887e-7) *
                             r + 1.8463183175100546818e-5) * r +
                            7.868691311456132591e-4) * r + .0148753612908506148525)
                          * r + .13692988092273580531) * r +
                         .59983220655588793769) * r + 1.0);
                }
                if (q < 0.0) val = -val;
            }
    
            return (mu + sigma * val);
        }
    
        private static bool R_Q_P01_boundaries(double p, double left, double right, bool isLowerTail, bool isLogValues, out double ans)
        {
            if (isLogValues)
            {
                if (p > 0.0)
                {
                    ans = double.NaN;
                    return (true);
                }
                if (p == 0.0)
                {
                    ans = isLowerTail ? right : left;
                    return (true);
                }
                if (p == double.NegativeInfinity)
                {
                    ans = isLowerTail ? left : right;
                    return (true);
                }
            }
            else
            {
                if (p < 0.0 || p > 1.0)
                {
                    ans = double.NaN;
                    return (true);
                }
                if (p == 0.0)
                {
                    ans = isLowerTail ? left : right;
                    return (true);
                }
                if (p == 1.0)
                {
                    ans = isLowerTail ? right : left;
                    return (true);
                }
            }
            ans = double.NaN;
            return (false);
        }
    
        private static double R_DT_qIv(double p, bool isLowerTail, bool isLogValues)
        {
            return (isLogValues ? (isLowerTail ? Math.Exp(p) : -ExpM1(p)) : R_D_Lval(p, isLowerTail));
        }
    
        private static double R_DT_CIv(double p, bool isLowerTail, bool isLogValues)
        {
            return (isLogValues ? (isLowerTail ? -ExpM1(p) : Math.Exp(p)) : R_D_Cval(p, isLowerTail));
        }
    
        private static double R_D_Lval(double p, bool isLowerTail)
        {
            return isLowerTail ? p : 0.5 - p + 0.5;
        }
    
        private static double R_D_Cval(double p, bool isLowerTail)
        {
            return isLowerTail ? 0.5 - p + 0.5 : p;
        }
        private static double ExpM1(double x)
        {
            if (Math.Abs(x) < 1e-5)
                return x + 0.5 * x * x;
            else
                return Math.Exp(x) - 1.0;
        }
    

    我针对 Excel 2016 中的 NORM.INV 实施对该方法的结果进行了一些初步的测试。准确度似乎相同,精度为 10^-7。

    【讨论】:

      猜你喜欢
      • 2010-10-28
      • 2018-03-20
      • 1970-01-01
      • 2011-04-05
      • 2013-03-11
      • 1970-01-01
      • 1970-01-01
      • 2018-08-17
      • 2020-11-15
      相关资源
      最近更新 更多