github.com/gopherd/gonum@v0.0.4/mathext/internal/cephes/zeta.go (about) 1 // Derived from SciPy's special/cephes/zeta.c 2 // https://github.com/scipy/scipy/blob/master/scipy/special/cephes/zeta.c 3 // Made freely available by Stephen L. Moshier without support or guarantee. 4 5 // Use of this source code is governed by a BSD-style 6 // license that can be found in the LICENSE file. 7 // Copyright ©1984, ©1987 by Stephen L. Moshier 8 // Portions Copyright ©2016 The Gonum Authors. All rights reserved. 9 10 package cephes 11 12 import "math" 13 14 // zetaCoegs are the expansion coefficients for Euler-Maclaurin summation 15 // formula: 16 // \frac{(2k)!}{B_{2k}} 17 // where 18 // B_{2k} 19 // are Bernoulli numbers. 20 var zetaCoefs = [...]float64{ 21 12.0, 22 -720.0, 23 30240.0, 24 -1209600.0, 25 47900160.0, 26 -1.307674368e12 / 691, 27 7.47242496e10, 28 -1.067062284288e16 / 3617, 29 5.109094217170944e18 / 43867, 30 -8.028576626982912e20 / 174611, 31 1.5511210043330985984e23 / 854513, 32 -1.6938241367317436694528e27 / 236364091, 33 } 34 35 // Zeta computes the Riemann zeta function of two arguments. 36 // Zeta(x,q) = \sum_{k=0}^{\infty} (k+q)^{-x} 37 // Note that Zeta returns +Inf if x is 1 and will panic if x is less than 1, 38 // q is either zero or a negative integer, or q is negative and x is not an 39 // integer. 40 // 41 // Note that: 42 // zeta(x,1) = zetac(x) + 1 43 func Zeta(x, q float64) float64 { 44 // REFERENCE: Gradshteyn, I. S., and I. M. Ryzhik, Tables of Integrals, Series, 45 // and Products, p. 1073; Academic Press, 1980. 46 if x == 1 { 47 return math.Inf(1) 48 } 49 50 if x < 1 { 51 panic(paramOutOfBounds) 52 } 53 54 if q <= 0 { 55 if q == math.Floor(q) { 56 panic(errParamFunctionSingularity) 57 } 58 if x != math.Floor(x) { 59 panic(paramOutOfBounds) // Because q^-x not defined 60 } 61 } 62 63 // Asymptotic expansion: http://dlmf.nist.gov/25.11#E43 64 if q > 1e8 { 65 return (1/(x-1) + 1/(2*q)) * math.Pow(q, 1-x) 66 } 67 68 // The Euler-Maclaurin summation formula is used to obtain the expansion: 69 // 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}} 70 // where 71 // B_{2j} 72 // are Bernoulli numbers. 73 // Permit negative q but continue sum until n+q > 9. This case should be 74 // handled by a reflection formula. If q<0 and x is an integer, there is a 75 // relation to the polyGamma function. 76 s := math.Pow(q, -x) 77 a := q 78 i := 0 79 b := 0.0 80 for i < 9 || a <= 9 { 81 i++ 82 a += 1.0 83 b = math.Pow(a, -x) 84 s += b 85 if math.Abs(b/s) < machEp { 86 return s 87 } 88 } 89 90 w := a 91 s += b * w / (x - 1) 92 s -= 0.5 * b 93 a = 1.0 94 k := 0.0 95 for _, coef := range zetaCoefs { 96 a *= x + k 97 b /= w 98 t := a * b / coef 99 s = s + t 100 t = math.Abs(t / s) 101 if t < machEp { 102 return s 103 } 104 k += 1.0 105 a *= x + k 106 b /= w 107 k += 1.0 108 } 109 return s 110 }