Meet 2 Blog
Created | |
---|---|
Tags |
Table of Contents
Introduction
Mathematicians have tried in vain to this day to discover some order in the sequence of prime numbers, and we have reason to believe that it is a mystery into which the human mind will never penetrate. — Leonhard Euler
Well that is indeed true. Primes numbers pose some of the notoriously hard problems in the field of Number Theory.
In this meet, we will mostly talk about prime numbers - how to find and use them - especially in the context of Competitive Programming.
So what are we going to cover in the meet?
- How to find primes? (Sieve algorithms)
- Totient Function
- Integer Factorization algorithms
- Primality Testing
- ... and lots of problems on the above
How to find primes?
Problem: Given a number find all the primesm in the segment .
So how do we proceed?
Well, the earliest known algorithm for this above task is the Sieve of Eratosthenes.
A simple yet massively insightful algorithm, it is still one of the most efficient ways to find all of the smaller primes.
Sieve of Eratosthenes
vi sieve(int n)
{
int is_composite[n + 5] = {0};
is_composite[0] = is_composite[1] = 1;
for (int i = 2; i * i <= n; i++)
if (!is_composite[i])
for (int j = i * i; j <= n; j += i)
is_composite[j] = 1;
vi primes;
for (int i = 0; i <= n; i++)
if (!is_composite[i])
primes.push_back(i);
return primes;
}
Complexity:
But can this be made faster?
Linear Sieve
Well there are indeed many ways to construct a sieve algorithm which uses linear time complexity i.e., rather than something of the form .
// input numbers
int n;
cin >> n;
int lp[n + 1];
vector<int> pr;
for (int i = 2; i <= N; i++)
{
if (lp[i] == 0)
lp[i] = i, pr.push_back(i);
for (int j = 0; j < (int)pr.size() && pr[j] <= lp[i] && i * pr[j] <= N; j++)
lp[i * pr[j]] = pr[j];
}
Sieve over a given range
Suppose, you need to find primes in the range where and .
#include <bits/stdc++.h>
#define int long long int
using namespace std;
#define vi vector<int>
// basic sieve
vi sieve(int n)
{
int is_composite[n + 5] = {0};
is_composite[0] = is_composite[1] = 1;
for (int i = 2; i * i <= n; i++)
if (!is_composite[i])
for (int j = i * i; j <= n; j += i)
is_composite[j] = 1;
vi primes;
for (int i = 0; i <= n; i++)
if (!is_composite[i])
primes.push_back(i);
return primes;
}
// sieve in a range
void sieve_in_range(int l, int r)
{
int sqrt_r = sqrt(r);
vi primes_till_root_r = sieve(sqrt_r);
vi is_prime(r - l + 1, true);
if (l == 1) is_prime[l - 1] = false;
for (int i: primes_till_root_r)
{
int x = (l / i) * i;
if (x < l) x += i;
for (int j = max(x, i * i); j <= r; j += i)
is_prime[j - l] = false;
}
for (int i = 0; i < is_prime.size(); i++)
if(is_prime[i])
cout << (l + i) << endl;
}
signed main()
{
// int n;
// cin >> n;
// sieve(n);
int l, r;
cin >> l >> r;
cout << "Required primes in given range are:" << endl;
sieve_in_range(l, r);
return 0;
}
Primality Testing
Problem: Check whether a given number is prime.
bool is_prime(int n)
{
for (int d = 2; d * d <= n; d++)
if (n % d == 0)
return false;
return true;
}
This algorithm has a time complexity of .
Miller-Rabin Primality Test
A moderately fine explanation of the below algorithm is present in https://cp-algorithms.com/algebra/primality_tests.html.
using u64 = uint64_t;
using u128 = __uint128_t;
u64 binpower(u64 base, u64 e, u64 mod)
{
u64 result = 1;
base %= mod;
while (e)
{
if (e & 1)
result = (u128)result * base % mod;
base = (u128)base * base % mod;
e >>= 1;
}
return result;
}
bool check_composite(u64 n, u64 a, u64 d, int s)
{
u64 x = binpower(a, d, n);
if (x == 1 || x == n - 1)
return false;
for (int r = 1; r < s; r++)
{
x = (u128)x * x % n;
if (x == n - 1)
return false;
}
return true;
}
bool MillerRabin(u64 n)
{
if (n < 2)
return false;
int r = 0;
u64 d = n - 1;
while ((d & 1) == 0)
{
d >>= 1, r++;
}
int arr[] = {2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37};
for (int a : arr)
{
if (n == a)
return true;
else if (check_composite(n, a, d, r))
return false;
}
return true;
}