A prime number is a natural number greater than 1 that has exactly two positive divisors: 1 and itself. Given a prime number N, the task is to check whether it can be expressed as the sum of two prime numbers.
- For an odd prime N, one of the two primes must be 2.
- Therefore, checking whether N - 2 is prime gives an efficient solution.
Illustration
Input: l = 10, r = 30
Output: 11 13 17 19 23 29
Explanation: The prime numbers between 10 and 30 are 11, 13, 17, 19, 23 and 29.
Input: l = 1, r = 10
Output: 2 3 5 7
Explanation: The prime numbers between 1 and 10 are 2, 3, 5, and 7.
Approaches to Find Prime Numbers in a Given Range
The following approaches can be used to find prime numbers between l and r:
1. Simple Trial Division
The simple approach checks every number in the range individually. For each number, we try dividing it by every integer from 2 to n - 1. If any value divides n completely, the number is not prime.
Approach
Traverse every number from l to r.
- For each number, check whether it has a divisor between 2 and n - 1.
- If no divisor is found, print the number as prime.
- Numbers less than or equal to 1 are not considered prime.
#include <stdbool.h>
#include <stdio.h>
bool isPrime(int n) {
// Checking primality by finding a complete division
// in the range 2 to n-1
if (n <= 1)
return false;
for (int i = 2; i < n; i++) {
if (n % i == 0)
return false;
}
return true;
}
void findPrimes(int l, int r) {
// Flag to check if any prime numbers are found
bool found = false;
for (int i = l; i <= r; i++) {
// Checking if the number is prime
if (isPrime(i)) {
printf("%d ", i);
found = true;
}
}
if (!found) {
printf(
"No prime numbers found in the given range.");
}
}
int main() {
int l = 10, r = 30;
// Finding and printing the prime between [l, r]
findPrimes(l, r);
return 0;
}
Output
11 13 17 19 23 29
2. Optimized Trial Division
The simple approach can be optimized using the fact that if a number n has a factor greater than its square root, it must also have a corresponding factor smaller than its square root.
Therefore, we only need to check divisors up to √n.
Approach
- Traverse every number from l to r.
- Return false for numbers less than or equal to 1.
- Handle 2 separately.
- Eliminate even numbers greater than 2.
- Check only odd divisors from 3 while i * i <= n.
- Print the number if no divisor is found.
#include <math.h>
#include <stdbool.h>
#include <stdio.h>
bool isPrime(int n) {
// Checking 1 and 2 for primality
if (n <= 1)
return false;
if (n == 2)
return true;
// Checking for divisiblity by 2
if (n % 2 == 0)
return false;
// Checking for divisiblity from 3 to sqrt(n)
for (int i = 3; i * i <= n; i += 1)
if (n % i == 0)
return false;
return true;
}
void findPrimes(int l, int r) {
// Flag to check if any prime numbers are found
bool found = false;
for (int i = l; i <= r; i++) {
if (isPrime(i)) {
// Print the prime number
printf("%d ", i);
found = true;
}
}
if (!found) {
printf("No prime numbers found in the given "
"range.");
}
}
int main() {
int l = 10, r = 30;
// Finding and printing prime number between [l, r]
findPrimes(l, r);
return 0;
}
Output
11 13 17 19 23 29
This approach is significantly faster than simple trial division because each number is tested only up to its square root.
3. Sieve of Eratosthenes
The Sieve of Eratosthenes finds all prime numbers up to a given limit efficiently. Instead of checking every number independently, it marks the multiples of each prime as non-prime.
For a range [l, r], we first generate all primes up to r and then print only those that lie within the given range.
Approach
- Create an array where each index represents a number from 0 to r.
- Initially consider all numbers prime.
- Mark 0 and 1 as non-prime.
- Starting from 2, mark all multiples of each prime as non-prime.
- Continue this process while i * i <= r.
- Traverse from l to r and print the numbers that remain marked as prime.
#include <math.h>
#include <stdbool.h>
#include <stdio.h>
#include <stdlib.h>
int* sieve(int n, int* size) {
// Allocate array to mark the multiples
bool* isPrime = (bool*)malloc((n + 1) * sizeof(bool));
// Initially, mark all the multiples as true
for (int i = 0; i <= n; i++) {
isPrime[i] = true;
}
// 0 and 1 are not prime numbers so mark
// them seperately
isPrime[0] = isPrime[1] = false;
// Sieve of Eratosthenes algorithm
for (int i = 2; i * i <= n; i++) {
if (isPrime[i]) {
for (int j = i * i; j <= n; j += i) {
// Mark multiples of i as non-prime
isPrime[j] = false;
}
}
}
// Count the number of prime numbers
int count = 0;
for (int i = 2; i <= n; i++) {
if (isPrime[i]) {
count++;
}
}
// Store all the prime numbers in seperate array
int* primes = (int*)malloc(count * sizeof(int));
int pi = 0;
for (int i = 2; i <= n; i++) {
if (isPrime[i]) {
primes[pi++] = i;
}
}
// Set the size of the primes array
*size = count;
free(isPrime);
return primes;
}
void findPrimes(int l, int r) {
int size;
// Get all primes up to r
int* primes = sieve(r, &size);
// If there are no prime numbers in the range
if (size == 0) {
printf("There are no prime numbers between the "
"given range!\n");
return;
}
// Starting from the prime number just after the l
int start = 0;
while (primes[start] < l) {
start++;
}
// Printing all prime numbers in the range
for (int i = start; i < size; i++) {
printf("%d ", primes[i]);
}
free(primes);
}
int main() {
int l = 10, r = 30;
// Finding and printing prime numbers in range [l, r]
findPrimes(l, r);
return 0;
}
Output
11 13 17 19 23 29
The sieve is especially useful when many prime numbers need to be found up to the same upper limit because the prime information can be generated together instead of testing each number separately.