我们如何在 Oracle SQL 或 PL/SQL 中实现 Standard Normal CDF?

Posted

技术标签:

【中文标题】我们如何在 Oracle SQL 或 PL/SQL 中实现 Standard Normal CDF?【英文标题】:How can we achieve Standard Normal CDF in Oracle SQL or PL/SQL? 【发布时间】:2017-07-31 13:15:31 【问题描述】:

如何使用 Oracle SQL 或 PL/SQL 实现以下功能?

【问题讨论】:

张贴文字,而不是图片。 公式很复杂这里不能打,所以贴上图片 懒惰在计算机编程中占有一席之地。这不是那个地方。 那么什么是“标准普通CDF”和“标准普通PDF”。我的意思是,我可以阅读公式,但我不知道在这种情况下 CDF 和 PDF 是什么。谢谢! @BobJarvis 那时你做得比我好;由于防火墙,我看不到图片。 (Vivek - 这就是我们要求文本而不是图像的部分原因。) 【参考方案1】:

此存储过程与 Calc 中的 NORMDIST 函数给出相同的结果。 需要传递的参数是x、均值、标准差和累积。 累积参数可以选择获取 x (0) 处的正态分布值或值

create or replace FUNCTION NORMDIST(x_value number,mean_value number,stddev_value number, cumulative NUMBER DEFAULT 0)
RETURN NUMBER IS

        x number;
        t number;
        z number;
        ans number;

BEGIN
  IF (stddev_value = 0) THEN
  RETURN 1;
  END IF;

  x := (x_value-mean_value)/stddev_value;

  IF cumulative = 1 THEN
    z := abs(x)/SQRT(2);
    t := 1/(1+0.5*z);
    ans := t*exp(-z*z-1.26551223+t*(1.00002368+t*(0.37409196+t*(0.09678418+t*(-0.18628806+t*(0.27886807+t*(-1.13520398+t*(1.48851587+t*(-0.82215223+t*0.17087277)))))))))/2;
    If (x <= 0)
     Then RETURN ans;
     Else return  1-ans;
    End if;
  ELSE

   RETURN 1/(sqrt(2*3.14159265358979)*stddev_value)*Exp(-(Power(x_value-mean_value,2)/(2*Power(stddev_value,2)) ));
  END IF;

END;
/

【讨论】:

【参考方案2】:

这是一个快速的解决方案,我没有尝试获得最大的精度或性能。根据您的要求,您可能需要调整数字格式、精度、计算逻辑等。

create or replace function calc_sn_pdf(x in number) return number
is
 pi      CONSTANT NUMBER := 3.14159265358979;
begin
  return 1/sqrt(2*pi) * exp(-x*x/2);
end;
/

cdf 必须近似(因为它是 az 积分函数,没有简单的数学公式),一种可能的近似实现如下。在 Wikipedia 上可以找到许多其他近似值。

create or replace function calc_sn_cdf(x in number) return number
is
 b0 CONSTANT NUMBER :=  0.2316419; 
 b1 CONSTANT NUMBER :=  0.319381530;
 b2 CONSTANT NUMBER := -0.356563782; 
 b3 CONSTANT NUMBER :=  1.781477937; 
 b4 CONSTANT NUMBER := -1.821255978; 
 b5 CONSTANT number :=  1.330274429;
 v_t  number;
begin
  --see 26.2.17 at http://people.math.sfu.ca/~cbm/aands/page_932.htm
  --see https://en.wikipedia.org/wiki/Normal_distribution#Numerical_approximations_for_the_normal_CDF
  --Zelen & Severo (1964) approximation
  if x < 0 then
    --this approximation works for x>0, but cdf is symmetric for x=0:
    return 1 - calc_sn_cdf(-x);
  else
    v_t := 1 / (1 + b0*x);
    return 1 - calc_sn_pdf(x)*(b1*v_t + b2*v_t*v_t + b3*v_t*v_t*v_t + b4*v_t*v_t*v_t*v_t + b5*v_t*v_t*v_t*v_t*v_t);
  end if;    
end;
/

顺便说一句,如果你需要大量运行这些函数,打开原生 pl/sql 编译会很有用。

【讨论】:

【参考方案3】:
--I wrote this function in PL/SQL and it works. I compared results with the NORMDIST 
 --Function in excel and the results match very closely. You will need to pass the  
  --following --parameters to the function.

-- 1. Value of X
-- 2. Value of Mean
-- 3. Value of Standard Deviation

--This function returns the same result  when you pass cumulative=TRUE in excel.


create or replace FUNCTION NORMSDIST(x_value number,mean_value number,stddev_value number)
RETURN NUMBER IS

        x number;
        t number;
        z number;
        ans number;

BEGIN
  IF (stddev_value = 0) THEN
  RETURN 1;
  END IF;

  x := (x_value-mean_value)/stddev_value;

  z := abs(x)/SQRT(2);

  t := 1.0/(1.0+0.5*z);

  ans := t*exp(-z*z-1.26551223+t*(1.00002368+t*(0.37409196+t*(0.09678418+t*(-0.18628806+t*(0.27886807+t*(-1.13520398+t*(1.48851587+t*(-0.82215223+t*0.17087277)))))))))/2.0;

     If (x <= 0)   
     Then RETURN ans;
     Else return  1-ans;
    End if; 

END NORMSDIST;

【讨论】:

以上是关于我们如何在 Oracle SQL 或 PL/SQL 中实现 Standard Normal CDF?的主要内容,如果未能解决你的问题,请参考以下文章

如何配置pl/sql 连接远程oracle服务器

Oracle SQL 或 PL/SQL:如何仅在上升趋势或下降趋势结束时识别烛台形态并在列中设置标志?

ORACLE 中的 PL/SQL、IF-ELSE 或 SELECT DECODE 哪个更好

如何在一组行之后或有条件地在没有 PL/SQL 块的情况下增加 oracle 序列?

oracle 11g ORA-06502:PL/SQL:数字或值错误:字符串缓冲区太小

调用封装成过程,Oracle PL/SQL