Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
Download

Testing latest pari + WASM + node.js... and it works?! Wow.

28495 views
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.