Primality Check
Description
Given a positive integer n, determine whether n is a prime number or not.
A prime number is a natural number greater than 1 that has no positive divisors other than 1 and itself. In other words, a prime number n can only be divided evenly (with zero remainder) by exactly two numbers: 1 and n.
For example, 7 is prime because the only numbers that divide it are 1 and 7. However, 6 is not prime because it can be divided by 2 and 3 (in addition to 1 and 6) — so 6 has more than two divisors.
The first few prime numbers are: 2, 3, 5, 7, 11, 13, 17, 19, 23, 29, ...
Note that 1 is NOT a prime number — by definition, primes must be greater than 1. Also, 2 is the only even prime number, since every other even number is divisible by 2.
Return true if n is prime, and false otherwise.
Examples
Example 1
Input: n = 17
Output: true
Explanation: We need to check whether any number between 2 and 16 divides 17 evenly:
- 17 % 2 = 1 ✗
- 17 % 3 = 2 ✗
- 17 % 4 = 1 ✗
- ... (no number from 2 to 16 divides 17)
- 17 % 16 = 1 ✗
No divisor found other than 1 and 17 itself. Therefore 17 is prime.
Example 2
Input: n = 56
Output: false
Explanation: We check if any number between 2 and 55 divides 56:
- 56 % 2 = 0 ✓ → 2 divides 56!
Since we found a divisor other than 1 and 56, the number 56 is NOT prime. We can stop immediately — finding even one extra divisor is enough to conclude the number is composite (not prime).
Example 3
Input: n = 1
Output: false
Explanation: By definition, prime numbers must be greater than 1. The number 1 has only one divisor (itself), not two. So 1 is not prime. This is an important edge case to handle before any divisibility checking.
Constraints
- 1 ≤ n ≤ 10^9
Editorial
Brute Force
Intuition
The most natural way to check if a number is prime is to follow the definition directly: a prime number has no divisors other than 1 and itself.
So we can try dividing n by every integer from 2 to n-1. If any of these numbers divides n evenly (remainder is zero), then n has an extra divisor and is NOT prime. If none of them divide n, then the only divisors are 1 and n — so n is prime.
Think of it like a trial: the number n is "innocent" (prime) unless proven "guilty" (composite). We put every candidate divisor from 2 to n-1 on the witness stand. If even one of them divides n perfectly, n is found guilty — it is composite. If all candidates fail to divide n, the number is declared innocent — it is prime.
Before starting the trial, we handle the edge case: if n ≤ 1, it cannot be prime by definition, so we immediately return false.
Step-by-Step Explanation
Let's trace through with n = 29:
Step 1: Check the edge case: is n ≤ 1? No (29 > 1), so we proceed.
Step 2: Start with i = 2. Is 29 % 2 == 0? No (remainder = 1). 2 does not divide 29. Move on.
Step 3: i = 3. Is 29 % 3 == 0? No (remainder = 2). Move on.
Step 4: i = 4. Is 29 % 4 == 0? No (remainder = 1). Move on.
Step 5: i = 5. Is 29 % 5 == 0? No (remainder = 4). Move on.
Step 6: Continue checking i = 6 through i = 13. None divide 29.
Step 7: i = 14. Is 29 % 14 == 0? No (remainder = 1). Move on.
Step 8: Continue checking i = 15 through i = 28. None divide 29 evenly.
Step 9: We have exhausted all candidates from 2 to 28, and none divided 29. The only divisors of 29 are 1 and 29 itself.
Result: 29 is prime. But notice: we performed 27 divisibility checks, and none were successful. For a large prime like 999999937, we would need over 999 million checks!
Brute Force — Checking Every Number from 2 to n-1 — Watch how we try dividing 29 by every candidate from 2 to 28. Since 29 is prime, every single check fails — demonstrating the worst case for brute force.
Algorithm
- If n ≤ 1, return false (not prime by definition)
- Iterate i from 2 to n-1:
- If n % i == 0, return false (found a divisor, so n is composite)
- If no divisor was found, return true (n is prime)
Code
#include <iostream>
using namespace std;
bool isPrime(int n) {
// Edge case: numbers <= 1 are not prime
if (n <= 1) return false;
// Check every number from 2 to n-1
for (int i = 2; i < n; i++) {
if (n % i == 0) {
return false; // Found a divisor, not prime
}
}
return true; // No divisor found, n is prime
}
int main() {
cout << isPrime(29) << endl; // Output: 1 (true)
cout << isPrime(56) << endl; // Output: 0 (false)
cout << isPrime(1) << endl; // Output: 0 (false)
return 0;
}def is_prime(n: int) -> bool:
# Edge case: numbers <= 1 are not prime
if n <= 1:
return False
# Check every number from 2 to n-1
for i in range(2, n):
if n % i == 0:
return False # Found a divisor, not prime
return True # No divisor found, n is prime
print(is_prime(29)) # Output: True
print(is_prime(56)) # Output: False
print(is_prime(1)) # Output: Falsepublic class PrimalityCheck {
public static boolean isPrime(int n) {
// Edge case: numbers <= 1 are not prime
if (n <= 1) return false;
// Check every number from 2 to n-1
for (int i = 2; i < n; i++) {
if (n % i == 0) {
return false; // Found a divisor, not prime
}
}
return true; // No divisor found, n is prime
}
public static void main(String[] args) {
System.out.println(isPrime(29)); // Output: true
System.out.println(isPrime(56)); // Output: false
System.out.println(isPrime(1)); // Output: false
}
}Complexity Analysis
Time Complexity: O(n)
In the worst case (when n is prime), we iterate from 2 to n-1, performing n-2 modulo operations. For a composite number, we might exit early when the first divisor is found (e.g., even numbers exit at i=2), but the worst case is always a prime number where every single candidate must be checked. For n = 10^9, this means roughly a billion iterations.
Space Complexity: O(1)
We only use a single loop variable i. No additional data structures are needed.
Why This Approach Is Not Efficient
The brute force checks every number from 2 to n-1. When n is prime (the worst case), we perform n-2 modulo operations. For n = 10^9, that is nearly one billion iterations — taking several seconds and exceeding typical time limits.
But consider what happens when we find a divisor. If some number d divides n (where 2 ≤ d ≤ n-1), then n/d is also a divisor. These divisors come in pairs: (d, n/d). Critically, in every such pair, at least one divisor must be ≤ √n.
Here is the reasoning: if both d and n/d were greater than √n, then d × (n/d) would be greater than √n × √n = n. But d × (n/d) = n, so we have a contradiction. Therefore, at least one factor in every pair is ≤ √n.
This means: if n has ANY divisor other than 1 and itself, at least one such divisor is ≤ √n. We do not need to check beyond √n!
For n = 10^9, √n ≈ 31,623. Instead of checking nearly a billion candidates, we only need to check about 31,623 — a speedup of over 30,000×.
Optimal Approach - Trial Division up to √n
Intuition
The key insight is based on the pairing property of divisors: if n = a × b, then one of a or b must be ≤ √n.
Imagine you are guarding a gate and need to verify that no unwanted visitor (divisor) exists. Instead of checking every person in a city of n residents (2 through n-1), you realize that divisors always arrive in pairs. If one partner in the pair is tall (> √n), the other must be short (≤ √n). So you only need to check the short side — everyone up to √n. If no short divisor exists, no tall divisor exists either, because they cannot exist alone.
So we iterate i from 2 to √n. If n % i == 0 for any i in this range, n is composite. If no such i exists, n is prime.
We can also handle small cases efficiently:
- If n ≤ 1: not prime
- If n == 2 or n == 3: prime (the smallest primes)
- If n is divisible by 2 or 3: not prime
After handling these, all remaining primes are of the form 6k ± 1 (i.e., one less or one more than a multiple of 6). This is because every integer is of the form 6k, 6k+1, 6k+2, 6k+3, 6k+4, or 6k+5. Among these, 6k is divisible by 6, 6k+2 and 6k+4 are divisible by 2, and 6k+3 is divisible by 3. So primes greater than 3 can only be 6k+1 or 6k+5 (which is the same as 6k-1).
Using this, we check divisibility by 2 and 3 first, then only check candidates of the form 6k ± 1, effectively skipping two-thirds of all candidates. This is the standard optimized trial division approach.
Step-by-Step Explanation
Let's trace through with n = 221 (√221 ≈ 14.87):
Step 1: Check edge cases: Is n ≤ 1? No. Is n == 2 or n == 3? No.
Step 2: Check divisibility by 2: Is 221 % 2 == 0? No (221 is odd). Move on.
Step 3: Check divisibility by 3: Is 221 % 3 == 0? No (2+2+1 = 5, not divisible by 3). Move on.
Step 4: Now check candidates of the form 6k ± 1, starting with i = 5. Check i = 5: Is 221 % 5 == 0? No (remainder = 1). Check i + 2 = 7: Is 221 % 7 == 0? No (remainder = 4). Move on.
Step 5: i = 11 (next 6k-1). Check 221 % 11 = 0? No (remainder = 1). Check i + 2 = 13: Is 221 % 13 == 0? Yes! 221 / 13 = 17.
Step 6: We found a divisor! 13 divides 221 (13 × 17 = 221). Since 13 ≤ √221 ≈ 14.87, we caught it within our √n range.
Result: 221 is NOT prime (it equals 13 × 17). We only checked 6 candidates (2, 3, 5, 7, 11, 13) instead of the brute force's 219 candidates (2 through 220).
Now let's also trace n = 29 (√29 ≈ 5.39) to see a prime case:
Step 1: Edge cases: 29 > 3, proceed.
Step 2: 29 % 2 = 1 → not divisible by 2.
Step 3: 29 % 3 = 2 → not divisible by 3.
Step 4: i = 5. Check 5: 29 % 5 = 4 → no. Since i = 5 and 5² = 25 ≤ 29, check i + 2 = 7: but 7² = 49 > 29, so we'd stop after checking 5.
Step 5: Actually, since i starts at 5 and i * i = 25 ≤ 29, we check 5 (no) and 7 (7*7=49 > 29 so the loop condition fails before checking 7). Loop ends.
Result: No divisor found up to √29. Therefore 29 IS prime. Only 3 actual divisibility checks (2, 3, 5) instead of 27!
Optimal — Trial Division up to √n with 6k±1 Optimization — Watch how we check only a handful of candidate divisors for n=221. Instead of checking 219 candidates, we check just 6 — and find that 13 divides 221.
Algorithm
- If n ≤ 1, return false
- If n == 2 or n == 3, return true (smallest primes)
- If n % 2 == 0 or n % 3 == 0, return false (divisible by 2 or 3)
- Iterate i from 5 while i × i ≤ n, incrementing by 6 each iteration:
- If n % i == 0, return false (found a divisor of the form 6k-1)
- If n % (i + 2) == 0, return false (found a divisor of the form 6k+1)
- If no divisor was found, return true (n is prime)
Code
#include <iostream>
using namespace std;
bool isPrime(int n) {
// Edge cases
if (n <= 1) return false;
if (n <= 3) return true; // 2 and 3 are prime
// Eliminate multiples of 2 and 3
if (n % 2 == 0 || n % 3 == 0) return false;
// Check candidates of the form 6k ± 1
// All primes > 3 are of this form
for (int i = 5; i * i <= n; i += 6) {
if (n % i == 0 || n % (i + 2) == 0) {
return false;
}
}
return true;
}
int main() {
cout << isPrime(29) << endl; // Output: 1 (true)
cout << isPrime(221) << endl; // Output: 0 (false, 13*17)
cout << isPrime(1) << endl; // Output: 0 (false)
cout << isPrime(2) << endl; // Output: 1 (true)
return 0;
}def is_prime(n: int) -> bool:
# Edge cases
if n <= 1:
return False
if n <= 3:
return True # 2 and 3 are prime
# Eliminate multiples of 2 and 3
if n % 2 == 0 or n % 3 == 0:
return False
# Check candidates of the form 6k ± 1
# All primes > 3 are of this form
i = 5
while i * i <= n:
if n % i == 0 or n % (i + 2) == 0:
return False
i += 6
return True
print(is_prime(29)) # Output: True
print(is_prime(221)) # Output: False (13*17)
print(is_prime(1)) # Output: False
print(is_prime(2)) # Output: Truepublic class PrimalityCheckOptimal {
public static boolean isPrime(int n) {
// Edge cases
if (n <= 1) return false;
if (n <= 3) return true; // 2 and 3 are prime
// Eliminate multiples of 2 and 3
if (n % 2 == 0 || n % 3 == 0) return false;
// Check candidates of the form 6k ± 1
// All primes > 3 are of this form
for (int i = 5; i * i <= n; i += 6) {
if (n % i == 0 || n % (i + 2) == 0) {
return false;
}
}
return true;
}
public static void main(String[] args) {
System.out.println(isPrime(29)); // Output: true
System.out.println(isPrime(221)); // Output: false (13*17)
System.out.println(isPrime(1)); // Output: false
System.out.println(isPrime(2)); // Output: true
}
}Complexity Analysis
Time Complexity: O(√n)
The loop runs from i = 5 while i × i ≤ n. Since i increments by 6 each iteration, the number of iterations is approximately √n / 6. Each iteration performs two modulo operations (checking i and i+2). In Big-O terms, this simplifies to O(√n). For n = 10^9, we perform roughly √(10^9) / 3 ≈ 10,541 modulo operations — a massive improvement over the brute force's billion operations.
For composite numbers, the function returns even faster: as soon as the first divisor is found, we exit immediately. Only prime numbers require the full √n scan.
Space Complexity: O(1)
We only use a single loop variable i and a few comparisons. No additional data structures are needed regardless of the input size.