Problem overview
The function is_prime takes an integer n and returns true when it is prime and false otherwise. A prime is a number greater than 1 whose only divisors are 1 and itself. So 31 is prime, while 6 is not, because 2 and 3 divide it.
The statement asks for O(n) time and O(1) extra space. That allows a plain loop that tests every candidate divisor.
Approach
Turn the definition around. A number n is prime exactly when no number d with 1 < d < n divides it. So the function tries each d from 2 up to n - 1. The first divisor it finds proves that n is not prime, and the function can return false at once. If the loop ends without finding one, n is prime.
A number d divides n when the remainder of n / d is zero, which Rust writes as n % d == 0.
One input needs separate handling. Prime numbers start at 2, so 0, 1 and every negative number must give false. The loop alone would get these wrong, as the edge cases below show.
Two examples traced
For n = 6, the loop starts with d = 2. Since 6 % 2 is 0, the function returns false immediately, and the values 3, 4 and 5 are never tried.
For n = 31, the remainders for d from 2 to 30 are all non-zero. For instance, 31 % 2 is 1, 31 % 3 is 1 and 31 % 5 is 1. The loop finishes without returning, and the final line returns true.
Why this is the simple version
The loop tests more candidates than it needs to. Any divisor of n above its square root pairs with a divisor below it, so a faster check could stop at the square root. This lesson keeps the straightforward loop, which matches the O(n) budget and makes the definition easy to see in the code.
Step by step
- Reject numbers below 2. Check
n < 2first and returnfalse. This covers 0, 1 and the negatives. - Loop over the candidate divisors. The variable
druns from 2 up ton - 1. Inside the loop, test whethern % d == 0. - Return
falseon the first divisor. One divisor between 2 andn - 1is enough to rule out a prime, so there is no reason to keep looping. - Return
trueafter the loop. Reaching this line means noddividedn.
The solution in Rust
impl Solution {
fn is_prime(n: i32) -> bool {
if n < 2 {
return false;
}
for d in 2..n {
if n % d == 0 {
return false;
}
}
return true;
}
}
Some Rust details here:
2..nexcludesn. The rangea..bincludesaand stops beforeb, sodtakes the values 2 ton - 1. Writing2..=nwould includenitself, and every number would then look divisible by itself.- An empty range is fine. When
nis 2, the range2..2has no values, so the body never runs and the function returnstrue. A range whose start is past its end, such as2..1, is also empty and does not panic. - The type of
dis inferred. Becausen % dmixesdwith ani32, the compiler makesdani32too. - Early
return.return false;leaves the function from inside the loop. The finalreturn true;could also be written as the bare expressiontrue, withoutreturnand the semicolon. %keeps the sign of the dividend. In Rust,-4 % 2is0and-5 % 3is-2. This is why then < 2check has to come before the loop.
Complexity
Time: O(n). In the worst case, which is a prime, the loop runs n - 2 times and does one remainder operation each time. For a composite number the loop stops sooner, at its smallest divisor.
Space: O(1). The function keeps only n and the loop variable d, no matter how large n is.
Edge cases and common mistakes
Most wrong answers on this problem come from the boundaries of the range or from the inputs below 2.
- Forgetting the guard. Without the
n < 2check,is_prime(1)returnstrue, because the range2..1is empty and the loop is skipped. The same happens for 0 and for negative numbers. - Starting at 1. Every number is divisible by 1, so a loop that begins there rejects everything. Start at 2.
- Going up to
ninclusive. Everyndivides itself, so2..=nmakes every number composite. - The number 2. It is the smallest prime and the only even one. The empty loop gives the correct
true. - Using
=for==.if n % d = 0does not compile. Comparison needs==.
Running time in practice
Linear time is fine for small inputs, but it grows with the value of n, not with the number of digits. A prime near the top of the i32 range, about 2.1 billion, would need roughly two billion remainder operations. Composite numbers finish much sooner, because they stop at their smallest factor.
Testing
n = 31givestrue.n = 6givesfalse.n = 2givestrue, andn = 3givestrue.n = 4givesfalse.n = 1andn = 0givefalse.n = 97givestrue.
