Ciao, Shane,

On Sat, Nov 6, 2021 at 1:31 PM Marco Bodrato <bodr...@mail.dm.unipi.it> wrote:
Il 2021-11-03 19:18 Shane Neph ha scritto:
> mpz_primorial_ui( ) is a great function.  Why not return the actual
> number of primes that go into that calculation?

It may make sense, but only if we add also another function: a way to
compute the number of primes in a range; or at least in the
range [1..n], i.e. pi(n) for a given unsigned long n.

Il 2021-11-06 22:29 Shane Neph ha scritto:
That would be a nice solution.

If you really need both the primorial and the prime count, my solution is somehow a waste, because no memory of the sieved numbers is kept between the library calls. The numbers are sieved twice...

I got inspired by a piece of code that Seth Troisi sent a couple of years ago and I wrote a gmp_pi_ui(n) function with two variants, a trivial one (it calls gmp_primesieve) and an implementation of the Legendre strategy.

I attach it for comments. Compiled with -DMAIN it prints the number of primes up to increasing powers of two. On my computer, with small ranges it is slower than GP/Pari, for large ranges, it is faster...

Ĝis,
m
/* pi (N) -- Returns the number of primes in the range [1..N].

Copyright 2019-2021 Marco Bodrato.

 */

#include "gmp-impl.h"
#include "longlong.h"

#ifndef PI_LEGENDRE_THRESHOLD
#define PI_LEGENDRE_THRESHOLD 80000
#endif

/* n_fto_bit (n) = ((n-1)&(-CNST_LIMB(2)))/3U-1 */
static mp_limb_t
n_fto_bit (mp_limb_t n) { return ((n-5)|1)/3U; }
/* n_fto_bit(x) + 2 = gmp_legendre_pi_phi_2 (x) */

static mp_size_t
primesieve_size (mp_limb_t n) { return n_fto_bit(n) / GMP_LIMB_BITS + 1; }

static mp_limb_t
limb_apprsqrt (mp_limb_t x)
{
  int s;

  ASSERT (x > 2);
  count_leading_zeros (s, x);
  s = (GMP_LIMB_BITS - s) >> 1;
  return ((CNST_LIMB(1) << (s - 1)) + (x >> 1 >> s));
}

/* phi(a, 0) = a
 * phi(a, b) = phi(a, b-1) - phi(a/p_b, b-1)
 * phi(a, 1) = phi(a, 0) - phi(a/2, 0) = a - a/2
 * phi(a, 2) = phi(a, 1) - phi(a/3, 1) = a - a/2 - (a/3 - a/6) ~= a/3
 * phi(a, b) = phi(a, 2) - sum_{1<c<b} phi(a/p_c, c)
 *
 * If a < p_b^2, phi(a, b) = pi(a) - b + 1. Not jet implemented.
 * Only the case a < p_{b+1}, then phi(a, b) = 1, is implemented;
 * slightly extended to 0 <= a-p_{b+1} is very small, phi (a,b) = 1+1.
 * 
 * This implementation receives primes = b - 2, the number of primes
 * in sieve (2 and 3 are precomputed and not counted in the sieve)
 */
static long
gmp_legendre_pi_phi_2 (unsigned long x /*,  0 (= b-2) */ )
{
  return ((x + 1) | 1) / 3UL;
}

static long
gmp_legendre_pi_phi(unsigned long n, unsigned primes, mp_ptr sieve)
{
  /* n -= 1 - (n & 1); /\* Reduce to odd *\/ */
  long res = gmp_legendre_pi_phi_2 (n);
  /* res precomputed for primes 2 and 3 */

  ASSERT (primes > 0);
  /* if (primes == 0) */
  /*   return res; */

  /* if (res * 3 == n) n -= 2; */
  unsigned count = 0;
  unsigned long i = 4, *sp = sieve;
  do { /* Loop on primes */
    for (unsigned long b = i, x = ~ *(sp++); x != 0; b += 3, x >>= 1)
      if (x & 1)
	{
	  /* b = 4 + 3*k :
	   * if k = 2n, b = 4 (mod 6) is even, the prime is b+1,
	   *    and b+2 = 0 (mod 6) is composite;
	   * if k = 2n+1, b = 1 (mod 6) is odd, b is the prime,
	   *    and b+1 (even), b+2 = 3 (mod 6), b+2+(b%2) (even) are composite.
	   */
	  unsigned long frac = n / (b | 1);

	  if (frac <= (b + 2 + (b & 1)))
	    return res - primes + count - (frac >= (b | 1));

	  if (count < 2) /* Directly compute, avoid recursion. */
	    res -= gmp_legendre_pi_phi_2 (frac) /* count == 0 */
	      - (count ? gmp_legendre_pi_phi_2 (frac / 5) : 0); /* count == 1 */
	  else
	    res -= gmp_legendre_pi_phi(frac, count, sieve);

	  if (primes == ++count)
	    return res;
	}
    i += GMP_LIMB_BITS * 3;
  } while (1);
}

/* Takes a pre-sieved range of primes [5..maxprime].
 * The range contains primes_in_sieve primes.
 *
 * Legendre showed that, if maxprime^2 >= n, then
 * primepi(n) = primepi(maxprime) + phi(n, primepi(maxprime)) - 1
 * where phi (a, b) = numbers x <= a, not divisible by the first b primes.
 *
 * The sieve does not count 2 and 3, so that
 * primepi(maxprime) = primes_in_sieve + 2
 *
 * Here we pass to pi_phi the prime index of b: primes_in_sieve.
 */
static long
gmp_legendre_pi_ps (unsigned long n, unsigned primes_in_sieve, mp_ptr sieve)
{
  return primes_in_sieve + 1 + gmp_legendre_pi_phi(n, primes_in_sieve, sieve);
}

static long
gmp_legendre_pi_ui(unsigned long n)
{
  unsigned long sqrt_n = limb_apprsqrt (n);
  unsigned sieve_size = primesieve_size (sqrt_n);
  TMP_SDECL;

  TMP_SMARK;
  mp_ptr sieve = TMP_SALLOC_LIMBS (sieve_size);
  unsigned count = gmp_primesieve (sieve, sqrt_n);
  long res = gmp_legendre_pi_ps (n, count, sieve);
  TMP_SFREE;

  return res;
}

/* TODO: the _primesieve interface should be extended, to allow
   piece-wise sieving. Storing the full sieve is wasteful here.
*/
static long
gmp_persieve_pi_ui (unsigned long n)
{
  mp_size_t size = primesieve_size (n);
  TMP_DECL;

  TMP_MARK;
  long res = gmp_primesieve (TMP_ALLOC_LIMBS (size), n);
  TMP_FREE;

  return res + 2;
}

long
gmp_pi_ui (unsigned long n)
{
  if (n < 7)
    return (n>1) + (n>2) + (n>4);
  if (BELOW_THRESHOLD (n, PI_LEGENDRE_THRESHOLD))
    return gmp_persieve_pi_ui (n);
  else
    return gmp_legendre_pi_ui (n);
}

#ifdef MAIN


int main() {
  unsigned step = 2;
  for (unsigned long n=1; n< ~0UL / step ; n *= step)
      gmp_printf ("%lu -> %ld\n", n, gmp_pi_ui (n));
}
#endif
_______________________________________________
gmp-devel mailing list
gmp-devel@gmplib.org
https://gmplib.org/mailman/listinfo/gmp-devel

Reply via email to