UNPKG

4.89 kBJavaScriptView Raw
1'use strict'
2
3const deepMap = require('../../utils/collection/deepMap')
4const isInteger = require('../../utils/number').isInteger
5
6function factory (type, config, load, typed) {
7 const multiply = load(require('../arithmetic/multiply'))
8 const pow = load(require('../arithmetic/pow'))
9 const product = require('./product')
10 /**
11 * Compute the gamma function of a value using Lanczos approximation for
12 * small values, and an extended Stirling approximation for large values.
13 *
14 * For matrices, the function is evaluated element wise.
15 *
16 * Syntax:
17 *
18 * math.gamma(n)
19 *
20 * Examples:
21 *
22 * math.gamma(5) // returns 24
23 * math.gamma(-0.5) // returns -3.5449077018110335
24 * math.gamma(math.i) // returns -0.15494982830180973 - 0.49801566811835596i
25 *
26 * See also:
27 *
28 * combinations, factorial, permutations
29 *
30 * @param {number | Array | Matrix} n A real or complex number
31 * @return {number | Array | Matrix} The gamma of `n`
32 */
33
34 const gamma = typed('gamma', {
35
36 'number': function (n) {
37 let t, x
38
39 if (isInteger(n)) {
40 if (n <= 0) {
41 return isFinite(n) ? Infinity : NaN
42 }
43
44 if (n > 171) {
45 return Infinity // Will overflow
46 }
47
48 return product(1, n - 1)
49 }
50
51 if (n < 0.5) {
52 return Math.PI / (Math.sin(Math.PI * n) * gamma(1 - n))
53 }
54
55 if (n >= 171.35) {
56 return Infinity // will overflow
57 }
58
59 if (n > 85.0) { // Extended Stirling Approx
60 const twoN = n * n
61 const threeN = twoN * n
62 const fourN = threeN * n
63 const fiveN = fourN * n
64 return Math.sqrt(2 * Math.PI / n) * Math.pow((n / Math.E), n) *
65 (1 + 1 / (12 * n) + 1 / (288 * twoN) - 139 / (51840 * threeN) -
66 571 / (2488320 * fourN) + 163879 / (209018880 * fiveN) +
67 5246819 / (75246796800 * fiveN * n))
68 }
69
70 --n
71 x = p[0]
72 for (let i = 1; i < p.length; ++i) {
73 x += p[i] / (n + i)
74 }
75
76 t = n + g + 0.5
77 return Math.sqrt(2 * Math.PI) * Math.pow(t, n + 0.5) * Math.exp(-t) * x
78 },
79
80 'Complex': function (n) {
81 let t, x
82
83 if (n.im === 0) {
84 return gamma(n.re)
85 }
86
87 n = new type.Complex(n.re - 1, n.im)
88 x = new type.Complex(p[0], 0)
89 for (let i = 1; i < p.length; ++i) {
90 const real = n.re + i // x += p[i]/(n+i)
91 const den = real * real + n.im * n.im
92 if (den !== 0) {
93 x.re += p[i] * real / den
94 x.im += -(p[i] * n.im) / den
95 } else {
96 x.re = p[i] < 0
97 ? -Infinity
98 : Infinity
99 }
100 }
101
102 t = new type.Complex(n.re + g + 0.5, n.im)
103 const twoPiSqrt = Math.sqrt(2 * Math.PI)
104
105 n.re += 0.5
106 const result = pow(t, n)
107 if (result.im === 0) { // sqrt(2*PI)*result
108 result.re *= twoPiSqrt
109 } else if (result.re === 0) {
110 result.im *= twoPiSqrt
111 } else {
112 result.re *= twoPiSqrt
113 result.im *= twoPiSqrt
114 }
115
116 const r = Math.exp(-t.re) // exp(-t)
117 t.re = r * Math.cos(-t.im)
118 t.im = r * Math.sin(-t.im)
119
120 return multiply(multiply(result, t), x)
121 },
122
123 'BigNumber': function (n) {
124 if (n.isInteger()) {
125 return (n.isNegative() || n.isZero())
126 ? new type.BigNumber(Infinity)
127 : bigFactorial(n.minus(1))
128 }
129
130 if (!n.isFinite()) {
131 return new type.BigNumber(n.isNegative() ? NaN : Infinity)
132 }
133
134 throw new Error('Integer BigNumber expected')
135 },
136
137 'Array | Matrix': function (n) {
138 return deepMap(n, gamma)
139 }
140 })
141
142 /**
143 * Calculate factorial for a BigNumber
144 * @param {BigNumber} n
145 * @returns {BigNumber} Returns the factorial of n
146 */
147
148 function bigFactorial (n) {
149 if (n.isZero()) {
150 return new type.BigNumber(1) // 0! is per definition 1
151 }
152
153 const precision = config.precision + (Math.log(n.toNumber()) | 0)
154 const Big = type.BigNumber.clone({ precision: precision })
155
156 let res = new Big(n)
157 let value = n.toNumber() - 1 // number
158 while (value > 1) {
159 res = res.times(value)
160 value--
161 }
162
163 return new type.BigNumber(res.toPrecision(type.BigNumber.precision))
164 }
165
166 gamma.toTex = { 1: `\\Gamma\\left(\${args[0]}\\right)` }
167
168 return gamma
169}
170
171// TODO: comment on the variables g and p
172
173const g = 4.7421875
174
175const p = [
176 0.99999999999999709182,
177 57.156235665862923517,
178 -59.597960355475491248,
179 14.136097974741747174,
180 -0.49191381609762019978,
181 0.33994649984811888699e-4,
182 0.46523628927048575665e-4,
183 -0.98374475304879564677e-4,
184 0.15808870322491248884e-3,
185 -0.21026444172410488319e-3,
186 0.21743961811521264320e-3,
187 -0.16431810653676389022e-3,
188 0.84418223983852743293e-4,
189 -0.26190838401581408670e-4,
190 0.36899182659531622704e-5
191]
192
193exports.name = 'gamma'
194exports.factory = factory