Source code for mathematicskit.number_theory.systems.prime_distribution
r"""The distribution of primes: the prime-counting function, the logarithmic
integral, and primes in arithmetic progressions.
Primes come from :func:`~mathematicskit.number_theory.systems.primality.sieve_of_eratosthenes`;
the logarithmic integral wraps :func:`scipy.special.expi`. See Apostol,
*Introduction to Analytic Number Theory*, Ch. 4 (the prime number
theorem) and Ch. 7 (Dirichlet's theorem).
"""
from __future__ import annotations
import numpy as np
from scipy.special import expi
from mathematicskit.number_theory.systems.primality import sieve_of_eratosthenes
__all__ = ["prime_counting", "logarithmic_integral", "primes_in_progression"]
[docs]
def prime_counting(x) -> np.ndarray:
r"""The prime-counting function :math:`\pi(x)`: the number of primes :math:`\le x`.
Sieves once up to ``max(x)`` and counts with
:func:`numpy.searchsorted`, so a whole array of ``x`` costs a single
sieve.
Parameters
----------
x : int or array_like of int
Non-negative arguments.
Returns
-------
int or ndarray of int
Same shape as `x`.
Examples
--------
>>> int(prime_counting(100))
25
>>> prime_counting([10, 100, 1000, 10000])
array([ 4, 25, 168, 1229])
"""
xs = np.asarray(x, dtype=np.int64)
primes = sieve_of_eratosthenes(int(xs.max(initial=0)))
return np.searchsorted(primes, xs, side="right")
[docs]
def logarithmic_integral(x):
r"""The logarithmic integral :math:`\operatorname{li}(x) = \int_0^x \frac{dt}{\ln t}`.
The (Cauchy principal value) integral equals :math:`\operatorname{Ei}(\ln x)`,
evaluated with :func:`scipy.special.expi`. Gauss's conjectured
approximation to :math:`\pi(x)`: the prime number theorem (Hadamard
and de la Vallée Poussin, 1896) states :math:`\pi(x) \sim
\operatorname{li}(x) \sim x/\ln x`.
Parameters
----------
x : float or array_like
``x > 0``.
Returns
-------
float or ndarray
Examples
--------
>>> round(float(logarithmic_integral(2.0)), 6) # li(2), the Ramanujan-Soldner offset
1.045164
>>> round(float(logarithmic_integral(1e6))) # vs. pi(10**6) = 78498
78628
"""
return expi(np.log(x))
[docs]
def primes_in_progression(a: int, q: int, limit: int) -> np.ndarray:
r"""Primes :math:`p \le` `limit` with :math:`p \equiv a \pmod q`.
Dirichlet's theorem (1837) guarantees infinitely many such primes
whenever :math:`\gcd(a, q) = 1`; the prime number theorem for
arithmetic progressions sharpens this to each of the
:math:`\varphi(q)` coprime residue classes receiving an asymptotic
share :math:`1/\varphi(q)` of all primes.
Parameters
----------
a : int
Residue class.
q : int
Modulus, ``q >= 1``.
limit : int
Returns
-------
ndarray of int
The primes in ``[2, limit]`` congruent to ``a`` mod ``q``, ascending.
Examples
--------
>>> primes_in_progression(3, 4, 50)
array([ 3, 7, 11, 19, 23, 31, 43, 47])
>>> primes_in_progression(2, 4, 1000) # gcd(2, 4) = 2: only the prime 2
array([2])
"""
if q < 1:
raise ValueError("q must be >= 1")
primes = sieve_of_eratosthenes(limit)
return primes[primes % q == a % q]