Testing latest pari + WASM + node.js... and it works?! Wow.
License: GPL3
ubuntu2004
Function: limitnum
Section: sums
C-Name: limitnum0
Prototype: GDGp
Help: limitnum(expr,{alpha=1}): numerical limit of sequence expr
using Lagrange-Zagier extrapolation; assume u(n) ~ sum a_i n^(-alpha*i).
Doc: Lagrange-Zagier numerical extrapolation of \var{expr}, corresponding to
a sequence $u_n$, either given by a closure \kbd{n->u(n)}. I.e., assuming
that $u_n$ tends to a finite limit $\ell$, try to determine $\ell$.
The routine assume that $u_n$ has an asymptotic expansion in $n^{-\alpha}$ :
$$u_n = \ell + \sum_{i\geq 1} a_i n^{-i\alpha}$$
for some $a_i$. It is purely numerical and heuristic, thus may or may not
work on your examples. The expression will be evaluated for $n = 1, 2,
\dots, N$ for an $N = O(B)$ at a bit accuracy bounded by $1.612 B$.
\bprog
? limitnum(n -> n*sin(1/n))
%1 = 1.0000000000000000000000000000000000000
? limitnum(n -> (1+1/n)^n) - exp(1)
%2 = 0.E-37
? limitnum(n -> 2^(4*n+1)*(n!)^4 / (2*n)! /(2*n+1)! ) - Pi
%3 = 0.E -37
@eprog\noindent
It is not mandatory to specify $\alpha$ when the $u_n$ have an asymptotic
expansion in $n^{-1}$. However, if the series in $n^{-1}$ is lacunary,
specifying $\alpha$ allows faster computation:
\bprog
? \p1000
? limitnum(n->(1+1/n^2)^(n^2)) - exp(1)
time = 1min, 44,681 ms.
%4 = 0.E-1001
? limitnum(n->(1+1/n^2)^(n^2), 2) - exp(1)
time = 27,271 ms.
%5 = 0.E-1001 \\ still perfect, 4 times faster
@eprog\noindent
When $u_n$ has an asymptotic expansion in $n^{-\alpha}$ with $\alpha$ not an
integer, leaving $\alpha$ unspecified will bring an inexact limit. Giving a
satisfying optional argument improves precision; the program runs faster when
the optional argument gives non lacunary series.
\bprog
? \p50
? limitnum(n->(1+1/n^(7/2))^(n^(7/2))) - exp(1)
time = 982 ms.
%6 = 4.13[...] E-12
? limitnum(n->(1+1/n^(7/2))^(n^(7/2)), 1/2) - exp(1)
time = 16,745 ms.
%7 = 0.E-57
? limitnum(n->(1+1/n^(7/2))^(n^(7/2)), 7/2) - exp(1)
time = 105 ms.
%8 = 0.E-57
@eprog\noindent
Alternatively, $u_n$ may be given by a closure $N\mapsto [u_1,\dots, u_N]$
which can often be programmed in a more efficient way, for instance
when $u_{n+1}$ is a simple function of the preceding terms:
\bprog
? \p2000
? limitnum(n -> 2^(4*n+1)*(n!)^4 / (2*n)! /(2*n+1)! ) - Pi
time = 1,755 ms.
%9 = 0.E-2003
? vu(N) = \\ exploit hypergeometric property
{ my(v = vector(N)); v[1] = 8./3;\
for (n=2, N, my(q = 4*n^2); v[n] = v[n-1]*q/(q-1));\
return(v);
}
? limitnum(vu) - Pi \\ much faster
time = 106 ms.
%11 = 0.E-2003
@eprog\noindent All sums and recursions can be handled in the same way.
In the above it is essential that $u_n$ be defined as a closure because
it must be evaluated at a higher precision than the one expected for the
limit. Make sure that the closure does not depend on a global variable which
would be computed at a priori fixed accuracy. For instance, precomputing
\kbd{v1 = 8.0/3} first and using \kbd{v1} in \kbd{vu} above would be wrong
because the resulting vector of values will use the accuracy of \kbd{v1}
instead of the ambient accuracy at which \kbd{limitnum} will call it.
Alternatively, and more clumsily, $u_n$ may be given by a vector of values:
it must be long and precise enough for the extrapolation
to make sense. Let $B$ be the current \kbd{realbitprecision}, the vector
length must be at least $1.102 B$ and the values computed with bit accuracy
$1.612 B$.
\bprog
? limitnum(vector(10,n,(1+1/n)^n))
*** ^--------------------
*** limitnum: nonexistent component in limitnum: index < 43
\\ at this accuracy, we must have at least 43 values
? limitnum(vector(43,n,(1+1/n)^n)) - exp(1)
%12 = 0.E-37
? v = vector(43);
? s = 0; for(i=1,#v, s += 1/i; v[i]= s - log(i));
? limitnum(v) - Euler
%15 = -1.57[...] E-16
? v = vector(43);
\\ ~ 128 bit * 1.612
? localbitprec(207);\
s = 0; for(i=1,#v, s += 1/i; v[i]= s - log(i));
? limitnum(v) - Euler
%18 = 0.E-38
@eprog
Because of the above problems, the preferred format is thus a closure,
given either a single value or the vector of values $[u_1,\dots,u_N]$. The
function distinguishes between the two formats by evaluating the closure
at $N\neq 1$ and $1$ and checking whether it yields vectors of respective
length $N$ and $1$ or not.
\misctitle{Warning} The expression is evaluated for $n = 1, 2, \dots, N$
for an $N = O(B)$ if the current bit accuracy is $B$. If it is not defined
for one of these values, translate or rescale accordingly:
\bprog
? limitnum(n->log(1-1/n)) \\ can't evaluate at n = 1 !
*** at top-level: limitnum(n->log(1-1/n))
*** ^-----------------------
*** in function limitnum: log(1-1/n)
*** ^----------
*** log: domain error in log: argument = 0
? limitnum(n->-log(1-1/(2*n)))
%19 = -6.11[...] E-58
@eprog
We conclude with a complicated example. Since the function is heuristic,
it is advisable to check whether it produces the same limit for
$u_n, u_{2n}, \dots u_{km}$ for a suitable small multiplier $k$.
The following function implements the recursion for the Motzkin numbers
$M_n$ which count the number of ways to draw non intersecting chords between
$n$ points on a circle:
$$ M_n = M_{n-1} + \sum_{i < n-1} M_i M_{n-2-i}
= ((n+1)M_{n-1}+(3n-3)M_{n-2}) / (n+2).$$
It is known that $M_n \sim \dfrac{3^{n+1}}{\sqrt{12\pi n^3}}$.
\bprog
\\ [M_k, M_{k*2}, ..., M_{k*N}] / (3^n / n^(3/2))
vM(N, k = 1) =
{ my(q = k*N, V);
if (q == 1, return ([1/3]));
V = vector(q); V[1] = V[2] = 1;
for(n = 2, q - 1,
V[n+1] = ((2*n + 1)*V[n] + 3*(n - 1)*V[n-1]) / (n + 2));
f = (n -> 3^n / n^(3/2));
return (vector(N, n, V[n*k] / f(n*k)));
}
? limitnum(vM) - 3/sqrt(12*Pi) \\ complete junk
%1 = 35540390.753542730306762369615276452646
? limitnum(N->vM(N,5)) - 3/sqrt(12*Pi) \\ M_{5n}: better
%2 = 4.130710262178469860 E-25
? limitnum(N->vM(N,10)) - 3/sqrt(12*Pi) \\ M_{10n}: perfect
%3 = 0.E-38
? \p2000
? limitnum(N->vM(N,10)) - 3/sqrt(12*Pi) \\ also at high accuracy
time = 409 ms.
%4 = 1.1048895470044788191 E-2004
@eprog\noindent In difficult cases such as the above a multiplier of 5 to 10
is usually sufficient. The above example is typical: a good multiplier usually
remains sufficient when the requested precision increases!
\synt{limitnum}{void *E, GEN (*u)(void *,GEN,long), GEN alpha, long prec}, where \kbd{u(E, n, prec)} must return $u(n)$ in precision \kbd{prec}.
Also available is
\fun{GEN}{limitnum0}{GEN u, GEN alpha, long prec}, where $u$
must be a vector of sufficient length as above.