Search code examples
scalafunctional-programmingprimesfor-comprehension

Check if number is prime in O(sqrt(n)) in Scala


When checking if n is a prime number in Scala, the most common solutions is concise one-liner which can be seen in almost all similar questions on SO

  def isPrime1(n: Int) = (n > 1) && ((2 until n) forall (n % _ != 0))

Moving on, it's simple to rewrite it to check only odd numbers

  def isPrime2(n: Int): Boolean = {
    if (n < 2) return false
    if (n == 2) return true
    if (n % 2 == 0) false
    else (3 until n by 2) forall (n % _ != 0)
  }

However, to be more efficient I would like to combine checking only odds with counting up to sqrt(n), but without using Math.sqrt. So, as i < sqrt(n) <==> i * i < n, I would write C-like loop:

def isPrime3(n: Int): Boolean = {
    if (n < 2) return false
    if (n == 2) return true
    if (n % 2 == 0) return false
    var i = 3
    while (i * i <= n) {
      if (n % i == 0) return false
      i += 2
    }
    true
  }

The questions are:

1) How to achieve the last version in the first version nice Scala functional style?

2) How can I use Scala for to this? I thought of something similar to below, but don't know how.

for {
    i <- 3 until n by 2
    if i * i <= n
  } { ??? }

Solution

  • Here is a method to verify if n is prime until sqrt(n) without using sqrt:

      def isPrime3(n: Int): Boolean = {
        if (n == 2) {
          true
        } else if (n < 2 || n % 2 == 0) {
          false
        } else {
          Stream.from(3, 2).takeWhile(i => i * i < n + 1).forall(i => n % i != 0)
        }
      }
    

    If you want to do it until n/2, which is also a possible optimization (worse than sqrt(n)), you can replace the last line with:

     (3 to n/2 + 1 by 2).forall(i => n % i != 0)
    

    If you prefer, you could also make a tail recursive version, something along these lines:

      import scala.annotation.tailrec
    
      def isPrime3(n: Int): Boolean = {
        if (n == 2 || n == 3) {
          true
        } else if (n < 2 || n % 2 == 0) {
          false
        } else {
          isPrime3Rec(n, 3)
        }
      }
    
      @tailrec
      def isPrime3Rec(n:Int, i: Int): Boolean = {
        (n % i != 0) && ((i * i > n) || isPrime3Rec(n, i + 2))
      }