[libmath-prime-util-perl] 30/43: Inverse Riemann R for nth_prime_approx

Partha P. Mukherjee ppm-guest at moszumanska.debian.org
Thu May 21 18:53:08 UTC 2015


This is an automated email from the git hooks/post-receive script.

ppm-guest pushed a commit to annotated tag v0.40
in repository libmath-prime-util-perl.

commit 51eb53406bf6c7a9418223b4175b5e64ac729e5c
Author: Dana Jacobsen <dana at acm.org>
Date:   Sun Apr 6 23:20:18 2014 -0700

    Inverse Riemann R for nth_prime_approx
---
 Changes                   |  4 ++++
 lib/Math/Prime/Util.pm    |  8 +++++---
 lib/Math/Prime/Util/PP.pm |  5 ++---
 util.c                    | 45 ++++++++++++++-------------------------------
 4 files changed, 25 insertions(+), 37 deletions(-)

diff --git a/Changes b/Changes
index 713c556..00611f2 100644
--- a/Changes
+++ b/Changes
@@ -16,6 +16,10 @@ Revision history for Perl module Math::Prime::Util
 
     - Speed up exp_mangoldt.
 
+    - nth_prime_approx uses inverse RiemannR in XS code for better accuracy.
+      Cippola 1902 is still used for PP and large values, with a slightly
+      more accurate third order correction.
+
 
 0.39  2014-03-01
 
diff --git a/lib/Math/Prime/Util.pm b/lib/Math/Prime/Util.pm
index c083865..d7a01af 100644
--- a/lib/Math/Prime/Util.pm
+++ b/lib/Math/Prime/Util.pm
@@ -1333,7 +1333,7 @@ While this method is thousands of times faster than generating primes, and
 doesn't involve big tables of precomputed values, it still can take a fair
 amount of time for large inputs.  Calculating the C<10^12th> prime takes
 about 1 second, the C<10^13th> prime takes under 10 seconds, and the
-C<10^14th> prime (3475385758524527) takes under one minute.  Think about
+C<10^14th> prime (3475385758524527) takes under 30 seconds.  Think about
 whether a bound or approximation would be acceptable, as they can be
 computed analytically.
 
@@ -1365,8 +1365,10 @@ for small C<n>.
   say "The one trillionth prime is ~ ", nth_prime_approx(10**12);
 
 Returns an approximation to the C<nth_prime> function, without having to
-generate any primes.  Uses the Cipolla 1902 approximation with two
-polynomials, plus a correction for small values to reduce the error.
+generate any primes.  For values where the nth prime is smaller than
+C<2^64>, an inverse Riemann R function is used.  For larger values, uses the
+Cipolla 1902 approximation with up to 2nd order terms, plus a third order
+correction.
 
 
 =head2 nth_twin_prime
diff --git a/lib/Math/Prime/Util/PP.pm b/lib/Math/Prime/Util/PP.pm
index f25fa77..bc3f0bb 100644
--- a/lib/Math/Prime/Util/PP.pm
+++ b/lib/Math/Prime/Util/PP.pm
@@ -1159,9 +1159,8 @@ sub nth_prime_approx {
   elsif ($n <      12000) { $approx +=  3.0 * $order; }
   elsif ($n <     150000) { $approx +=  2.1 * $order; }
   elsif ($n <  200000000) { $approx +=  0.0 * $order; }
-  else                    { $approx += -0.010 * $order; }
-  # $approx = -0.025 is better for the last, but it gives problems with some
-  # other code that always wants the asymptotic approximation to be >= actual.
+  else                    { $approx += -0.025 * $order; }
+  # If we want the asymptotic approximation to be >= actual, use -0.010.
 
   return int($approx + 0.5);
 }
diff --git a/util.c b/util.c
index 6a46b47..88a5efa 100644
--- a/util.c
+++ b/util.c
@@ -736,41 +736,24 @@ UV nth_prime_lower(UV n)
 
 UV nth_prime_approx(UV n)
 {
-  long double fn, flogn, flog2n, approx, order;
+  long double fn, flogn;
+  UV lo, hi;
 
   if (n < NPRIMES_SMALL)
     return primes_small[n];
 
-  fn     = (long double) n;
-  flogn  = logl(n);
-  flog2n = logl(flogn);    /* Note distinction between log_2(n) and log^2(n) */
-
-  /* Cipolla 1902:
-   *    m=0   fn * ( flogn + flog2n - 1 );
-   *    m=1   + ((flog2n - 2)/flogn) );
-   *    m=2   - (((flog2n*flog2n) - 6*flog2n + 11) / (2*flogn*flogn))
-   *    + O((flog2n/flogn)^3)
-   */
-
-  approx = fn * (  flogn + flog2n - 1.0
-                 + ((flog2n - 2.0) / flogn)
-                 - (((flog2n*flog2n) - 6.0*flog2n + 11.0) / (2*flogn*flogn))
-                );
-
-  /* Apply a correction */
-  order = flog2n / flogn;
-  order = order * order * order * fn;
-  if      (n <      259) { approx += 10.4  * order; }
-  else if (n <      775) { approx +=  7.52 * order; }
-  else if (n <     1271) { approx +=  5.6  * order; }
-  else if (n <     2000) { approx +=  5.2  * order; }
-  else if (n <     4000) { approx +=  4.3  * order; }
-  else if (n <    12000) { approx +=  3.0  * order; }
-  else if (n <   150000) { approx +=  2.1  * order; }
-  else if (n <200000000) {                          }
-  else                   { approx += -0.01 * order; } /* -0.25 is closer */
-
-  return (UV) floorl(approx + 0.5);
+  /* Binary search for inverse Riemann R */
+  fn    = (long double) n;
+  flogn = logl(n);
+  lo    = (UV) (fn * flogn);
+  hi    = (UV) (fn * flogn * 2 + 2);
+  if (hi <= lo) hi = UV_MAX;
+  while (lo < hi) {
+    UV mid = lo + (hi-lo)/2;
+    if (_XS_RiemannR(mid) < fn) lo = mid+1;
+    else                        hi = mid;
+  }
+  return lo;
 }
 
 

-- 
Alioth's /usr/local/bin/git-commit-notice on /srv/git.debian.org/git/pkg-perl/packages/libmath-prime-util-perl.git



More information about the Pkg-perl-cvs-commits mailing list