如何在Trino(或SQL)中计算高斯误差函数erf/erfc
在Trino里实现erf/erfc的实用方案(低精度场景)
你要的是不用插值的方案,下面几个都能满足需求,按省心程度排序:
1. 直接用Trino内置正态分布函数转换
这是最省事的方法,因为erf/erfc和标准正态分布的累积分布函数(CDF)有现成的数学关系:
erf(x) = 2 * 标准正态CDF(x*√2) - 1erfc(x) = 2 * 标准正态CDF(-x*√2)
Trino自带normal_cdf(mean, stddev, x)函数,标准正态分布就是mean=0、stddev=1,直接套公式就行:
-- 计算erf(x) SELECT 2 * normal_cdf(0, 1, x * sqrt(2)) - 1 AS erf_x; -- 计算erfc(x) SELECT 2 * normal_cdf(0, 1, -x * sqrt(2)) AS erfc_x;
这个方案完全依赖Trino原生函数,精度足够低精度场景用,代码也短,不用额外定义函数。
2. 用近似公式写SQL自定义函数
如果不想依赖正态分布函数,直接用工业界常用的近似公式拼出erfc(erf直接用1-erfc就行),这个公式的绝对误差小于1.5e-7,完全满足你的精度要求:
先定义erfc函数,再基于它定义erf:
CREATE OR REPLACE FUNCTION erfc(x DOUBLE) RETURNS DOUBLE LANGUAGE SQL AS $$ WITH params AS ( SELECT 0.3275911 AS p, 0.254829592 AS a1, -0.284496736 AS a2, 1.421413741 AS a3, -1.453152027 AS a4, 1.061405429 AS a5 ) SELECT CASE WHEN x < 0 THEN 2 - erfc(-x) -- 利用erfc的奇偶性简化计算 ELSE exp(-x*x) * (a1*t + a2*pow(t,2) + a3*pow(t,3) + a4*pow(t,4) + a5*pow(t,5)) END FROM params, (SELECT 1/(1 + p*x) AS t FROM params) t_calc $$; -- 基于erfc定义erf CREATE OR REPLACE FUNCTION erf(x DOUBLE) RETURNS DOUBLE LANGUAGE SQL AS $$ SELECT 1 - erfc(x) $$;
之后直接调用erf(1.0)或者erfc(0.5)就行,不用管插值的事。
3. 写Java UDF(有权限的话)
如果你的Trino集群允许自定义Java函数,直接调用Java标准库的Math.erf()和Math.erfc()最靠谱,精度最高。
先写个简单的Java类:
public class ErfFunctions { public static double erf(double x) { return Math.erf(x); } public static double erfc(double x) { return Math.erfc(x); } }
编译打包成JAR传到集群,然后注册函数:
CREATE FUNCTION erf(DOUBLE) RETURNS DOUBLE WITH JAR 'hdfs:///your/path/to/erf-functions.jar' CLASS 'com.yourpackage.ErfFunctions.erf'; CREATE FUNCTION erfc(DOUBLE) RETURNS DOUBLE WITH JAR 'hdfs:///your/path/to/erf-functions.jar' CLASS 'com.yourpackage.ErfFunctions.erfc';
这个方案适合有集群管理权限的场景,一劳永逸。
内容的提问来源于stack exchange,提问作者Walter Tross
相关产品推荐
相关产品推荐

