Problem overview
The method isPrime receives a non-negative Int named n and returns true if it is prime. A prime number is greater than 1 and divisible only by 1 and by itself. For example, 31 is prime, so the result is true. 6 is divisible by 2 and 3, so the result is false.
The statement asks for O(n) time and O(1) extra space. That means one pass over the numbers up to n is welcome, and no collection is needed.
Approach: look for a divisor
Restate the definition: n is prime when no number d with 1 < d < n divides it. That turns the problem into a search for a counterexample. Try every d from 2 to n - 1. If any of them divides n evenly, n is not prime and the answer is false right away. If none does, the answer is true.
A number d divides n exactly when the remainder n % d is 0.
The definition says primes are greater than 1, so the loop would give the wrong answer for 0 and 1: both have no candidate divisors in the range, and the loop would conclude that they are prime. They need their own check first.
Step by step
- Handle numbers below 2. If
n < 2, returnfalse. This covers 0 and 1, which are not prime. - Loop over the candidate divisors.
for (d <- 2 until n)givesdthe values 2, 3, up ton - 1. The range excludesnitself, which is correct because every number divides itself. - Test divisibility. Inside the loop, if
n % d == 0, thendis a divisor between 1 andn, and the method returnsfalse. - Return
true. If the loop ends, no divisor was found, sonis prime.
The solution in Scala
class Solution {
def isPrime(n: Int): Boolean = {
if (n < 2) {
return false
}
for (d <- 2 until n) {
if (n % d == 0) {
return false
}
}
return true
}
}
The Scala details worth knowing:
2 until ncan be empty. Forn = 2it is2 until 2, which has no elements. The loop body never runs and the method returnstrue, which is right since 2 is prime. The same happens for 3, whose only candidate is 2, and 2 doesn't divide 3.%is the remainder. Between twoIntvalues,n % dis what is left after integer division. If it is 0,ddividesn.- Early
returnin two places. The firstreturn falseis directly in the method body. The second sits inside theforbody, which Scala treats as a function passed toforeach. Scala 3.5 accepts it and the method works, but the compiler prints a deprecation warning about non-local returns. - The result type is satisfied. The method ends with
return true, so the compiler sees aBooleanon every path.
Tracing two inputs
For n = 9, the loop tries d = 2 first. The remainder of 9 divided by 2 is 1, so nothing happens. Then d = 3: the remainder is 0, so the method returns false and the candidates 4 to 8 are never tried.
For n = 7, the loop tries 2, 3, 4, 5 and 6. Each leaves a nonzero remainder, the range runs out, and the method returns true. A prime is the slowest case, because the method can't stop until every candidate has been rejected.
Complexity
Time: O(n). The loop makes at most n - 2 passes, each with one remainder operation. When n is composite the loop usually stops much sooner. It stops at the smallest divisor, and for an even number that is 2. The worst case is a prime, where every candidate has to be rejected.
Space: O(1). A range like 2 until n does not store its numbers, so no memory grows with n.
The loop can be made faster. If n has a divisor, it has one no larger than its square root, so it is enough to try d up to sqrt(n). This lesson keeps the plain version, because the simple search is the part worth learning first.
Edge cases and common mistakes
- 0 and 1. Not prime. Without the first check, they would reach the loop, find no divisor and be reported as prime.
- 2. The smallest prime, and the only even one. It returns
truebecause the range is empty. - Starting the loop at 1. Every number is divisible by 1, so
n % 1 == 0would make everynlook composite. - Including
nin the loop.2 to nreachesd = n, andn % n == 0always, so even primes would returnfalse. - Returning
trueinside the loop. One candidate that does not dividenproves nothing. Only the end of the loop does. - Very large inputs. For a prime near the top of the
Intrange the loop makes about two billion passes, so the square-root bound matters in practice.
Testing
isPrime(31)givestrue.isPrime(6)givesfalse.isPrime(2)givestrue.isPrime(1)andisPrime(0)givefalse.isPrime(9)givesfalse, because 3 divides it.
