在gamma()
R 中,您可以从输入 5 获得输出 24。如何制作可以从输入 24 获得输出 5 的反函数?
>gamma(5)
24
我找到了 [invgamma] 包,但我不明白它是否合适,也不知道如何使用它……
1
最佳答案
2
以下是两个使用 Newton-Raphson 的完全矢量化选项。第一个是在基础 R 中。第二个稍快一些,但需要包lamW
。两者都花费不到一秒钟来计算 1M 逆。
根据使用情况,您可能需要进行额外的检查,例如处理NA
或空值。
请注意,在 处x = gamma(y)
有最小值(x = 0.885603194410889
) ,并且当接近和时y = 1.46163214496836
接近,因此 有两个正解。下面的函数提供了 的值(“主分支”)。Inf
y
0
Inf
x = gamma(y)
x > 0.885603194410889
y
y > 1.46163214496836
基R
为开始牛顿-拉夫森迭代提供了一个不错的初始近似值。
gammainv <- function(x) {
i <- 1:length(x)
out <- numeric(length(x))
out[b <- (x < 0.885603194410889)] <- NA # minimum of gamma function
i <- i[!b]
x <- x[i]
out[i[b <- (x == 1)]] <- 2 # principal branch
i <- i[!b]
x <- x[!b]
x <- log(x)
# initial estimate
m <- x/log1p(x) + 1
k <- (lgamma(m) - x)/log(m)
y <- m - k - 1 + (x - lgamma(m - k))/log(m - k)
y[y < 1.46163214496836] <- 1.5 # principal branch
# Newton-Raphson
while (length(i)) {
y0 <- y
y <- y - (lgamma(y) - x)/digamma(y)
b <- which(abs(y - y0) < 1e-13)
if (length(b)) {
out[i[b]] <- y[b]
y <- y[-b]
i <- i[-b]
x <- x[-b]
}
}
out
}
测试:
1e6 - gamma(x <- gammainv(1e6))
#> [1] 2.211891e-09
dput(gamma(x))
#> 999999.999999998
gammainv(cumprod(1:170))
#> [1] 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
#> [19] 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37
#> [37] 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55
#> [55] 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73
#> [73] 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91
#> [91] 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109
#> [109] 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127
#> [127] 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145
#> [145] 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163
#> [163] 164 165 166 167 168 169 170 171
x <- exp(runif(1e6, log(0.885603194410889), 709))
system.time(y <- gammainv(x))
#> user system elapsed
#> 0.85 0.03 0.87
all.equal(x, gamma(y))
#> [1] TRUE
lamW
使用 Lambert W 函数来提高速度,从而提供更好的。
library(lamW)
gammainv <- function(x) {
i <- 1:length(x)
out <- numeric(length(x))
out[b <- (x < 0.885603194410889)] <- NA # minimum of gamma function
i <- i[!b]
logsqrt2pi <- log(2*pi)/2
x <- pmax(x, exp(logsqrt2pi - 1))
x <- log(x[i])
y <- exp(lambertW0((x - logsqrt2pi)/exp(1)) + 1) + 0.5
# Newton-Raphson
while (length(i)) {
y0 <- y
y <- y - (lgamma(y) - x)/digamma(y)
b <- which(abs(y - y0) < 1e-13)
if (length(b)) {
out[i[b]] <- y[b]
y <- y[-b]
i <- i[-b]
x <- x[-b]
}
}
out
}
测试
1e6 - gamma(y <- gammainv(1e6))
#> [1] 4.074536e-09
dput(gamma(y))
#> 999999.999999996
gammainv(cumprod(1:170))
#> [1] 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
#> [19] 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37
#> [37] 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55
#> [55] 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73
#> [73] 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91
#> [91] 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109
#> [109] 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127
#> [127] 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145
#> [145] 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163
#> [163] 164 165 166 167 168 169 170 171
x <- exp(runif(1e6, log(0.885603194410889), 709))
system.time(y <- gammainv(x))
#> user system elapsed
#> 0.73 0.05 0.56
all.equal(x, gamma(y))
#> [1] TRUE
3
-
有趣且高效的逆 Gamma 实现,+1!。您有关于代码的算法参考吗?最好在解决方案中显示它,以防其他人想知道它是如何/为什么工作的。
– -
我添加了几个讨论初始近似值的链接。
– -
看起来很棒,谢谢添加参考资料。
–
|
用uniroot
来求 的根gamma(x) - a
,其中a
是你的号码。
invgamma <- function(a, tol = .Machine$double.eps^0.5) {
uniroot(\(x) gamma(x) - a, c(1, 1e2), extendInt = "upX", tol = tol)
}
invgamma(24)$root
#> [1] 5
invgamma(24)
#> $root
#> [1] 5
#>
#> $f.root
#> [1] -5.207212e-11
#>
#> $iter
#> [1] 19
#>
#> $init.it
#> [1] NA
#>
#> $estim.prec
#> [1] 7.450582e-09
创建于 2024-09-29,使用
您可以矢量化该函数。
igamma <- Vectorize(invgamma, "a")
igamma(c(6, 24, 120)) |> t()
#> root f.root iter init.it estim.prec
#> [1,] 4 -2.771117e-13 17 NA 7.450582e-09
#> [2,] 5 -5.207212e-11 19 NA 7.450582e-09
#> [3,] 6 2.727063e-11 18 NA 7.450583e-09
创建于 2024-09-29,使用
|
invgamma
包用于逆伽马分布,而不是逆伽马函数。–
|