mirror of
https://github.com/gonum/gonum.git
synced 2025-10-11 01:50:12 +08:00
111 lines
2.6 KiB
Go
111 lines
2.6 KiB
Go
// Derived from SciPy's special/cephes/zeta.c
|
|
// https://github.com/scipy/scipy/blob/master/scipy/special/cephes/zeta.c
|
|
// Made freely available by Stephen L. Moshier without support or guarantee.
|
|
|
|
// Use of this source code is governed by a BSD-style
|
|
// license that can be found in the LICENSE file.
|
|
// Copyright ©1984, ©1987 by Stephen L. Moshier
|
|
// Portions Copyright ©2016 The Gonum Authors. All rights reserved.
|
|
|
|
package cephes
|
|
|
|
import "math"
|
|
|
|
// zetaCoegs are the expansion coefficients for Euler-Maclaurin summation
|
|
// formula:
|
|
// \frac{(2k)!}{B_{2k}}
|
|
// where
|
|
// B_{2k}
|
|
// are Bernoulli numbers.
|
|
var zetaCoefs = [...]float64{
|
|
12.0,
|
|
-720.0,
|
|
30240.0,
|
|
-1209600.0,
|
|
47900160.0,
|
|
-1.307674368e12 / 691,
|
|
7.47242496e10,
|
|
-1.067062284288e16 / 3617,
|
|
5.109094217170944e18 / 43867,
|
|
-8.028576626982912e20 / 174611,
|
|
1.5511210043330985984e23 / 854513,
|
|
-1.6938241367317436694528e27 / 236364091,
|
|
}
|
|
|
|
// Zeta computes the Riemann zeta function of two arguments.
|
|
// Zeta(x,q) = \sum_{k=0}^{\infty} (k+q)^{-x}
|
|
// Note that Zeta returns +Inf if x is 1 and will panic if x is less than 1,
|
|
// q is either zero or a negative integer, or q is negative and x is not an
|
|
// integer.
|
|
//
|
|
// Note that:
|
|
// zeta(x,1) = zetac(x) + 1
|
|
func Zeta(x, q float64) float64 {
|
|
// REFERENCE: Gradshteyn, I. S., and I. M. Ryzhik, Tables of Integrals, Series,
|
|
// and Products, p. 1073; Academic Press, 1980.
|
|
if x == 1 {
|
|
return math.Inf(1)
|
|
}
|
|
|
|
if x < 1 {
|
|
panic(badParamOutOfBounds)
|
|
}
|
|
|
|
if q <= 0 {
|
|
if q == math.Floor(q) {
|
|
panic(badParamFunctionSingularity)
|
|
}
|
|
if x != math.Floor(x) {
|
|
panic(badParamOutOfBounds) // Because q^-x not defined
|
|
}
|
|
}
|
|
|
|
// Asymptotic expansion: http://dlmf.nist.gov/25.11#E43
|
|
if q > 1e8 {
|
|
return (1/(x-1) + 1/(2*q)) * math.Pow(q, 1-x)
|
|
}
|
|
|
|
// The Euler-Maclaurin summation formula is used to obtain the expansion:
|
|
// Zeta(x,q) = \sum_{k=1}^n (k+q)^{-x} + \frac{(n+q)^{1-x}}{x-1} - \frac{1}{2(n+q)^x} + \sum_{j=1}^{\infty} \frac{B_{2j}x(x+1)...(x+2j)}{(2j)! (n+q)^{x+2j+1}}
|
|
// where
|
|
// B_{2j}
|
|
// are Bernoulli numbers.
|
|
// Permit negative q but continue sum until n+q > 9. This case should be
|
|
// handled by a reflection formula. If q<0 and x is an integer, there is a
|
|
// relation to the polyGamma function.
|
|
s := math.Pow(q, -x)
|
|
a := q
|
|
i := 0
|
|
b := 0.0
|
|
for i < 9 || a <= 9 {
|
|
i++
|
|
a += 1.0
|
|
b = math.Pow(a, -x)
|
|
s += b
|
|
if math.Abs(b/s) < machEp {
|
|
return s
|
|
}
|
|
}
|
|
|
|
w := a
|
|
s += b * w / (x - 1)
|
|
s -= 0.5 * b
|
|
a = 1.0
|
|
k := 0.0
|
|
for _, coef := range zetaCoefs {
|
|
a *= x + k
|
|
b /= w
|
|
t := a * b / coef
|
|
s = s + t
|
|
t = math.Abs(t / s)
|
|
if t < machEp {
|
|
return s
|
|
}
|
|
k += 1.0
|
|
a *= x + k
|
|
b /= w
|
|
k += 1.0
|
|
}
|
|
return s
|
|
}
|