Find prime numbers using Scala. Help me to improve

前端 未结 10 953
北荒
北荒 2020-12-01 22:31

I wrote this code to find the prime numbers less than the given number i in scala.

def findPrime(i : Int) : List[Int] = i match {
    case 2 => List(2)
           


        
相关标签:
10条回答
  • 2020-12-01 22:47

    A scalar fp approach

    // returns the list of primes below `number`
    def primes(number: Int): List[Int] = {
        number match {
            case a
            if (a <= 3) => (1 to a).toList
            case x => (1 to x - 1).filter(b => isPrime(b)).toList
        }
    }
    
    // checks if a number is prime
    def isPrime(number: Int): Boolean = {
        number match {
            case 1 => true
            case x => Nil == {
                2 to math.sqrt(number).toInt filter(y => x % y == 0)
            }
        }
    }
    
    0 讨论(0)
  • 2020-12-01 22:55
      /**
       * @return Bitset p such that p(x) is true iff x is prime
       */
      def sieveOfEratosthenes(n: Int) = {
        val isPrime = mutable.BitSet(2 to n: _*)
        for (p <- 2 to Math.sqrt(n) if isPrime(p)) {
          isPrime --= p*p to n by p
        }
        isPrime.toImmutable
      }
    
    0 讨论(0)
  • 2020-12-01 22:56

    The style looks fine to me. Although the Sieve of Eratosthenes is a very efficient way to find prime numbers, your approach works well too, since you are only testing for division against known primes. You need to watch out however--your recursive function is not tail recursive. A tail recursive function does not modify the result of the recursive call--in your example you prepend to the result of the recursive call. This means that you will have a long call stack and so findPrime will not work for large i. Here is a tail-recursive solution.

    def primesUnder(n: Int): List[Int] = {
      require(n >= 2)
    
      def rec(i: Int, primes: List[Int]): List[Int] = {
        if (i >= n) primes
        else if (prime(i, primes)) rec(i + 1, i :: primes)
        else rec(i + 1, primes)
      }
    
      rec(2, List()).reverse
    }
    
    def prime(num: Int, factors: List[Int]): Boolean = factors.forall(num % _ != 0)
    

    This solution isn't prettier--it's more of a detail to get your solution to work for large arguments. Since the list is built up backwards to take advantage of fast prepends, the list needs to be reversed. As an alternative, you could use an Array, Vector or a ListBuffer to append the results. With the Array, however, you would need to estimate how much memory to allocate for it. Fortunately we know that pi(n) is about equal to n / ln(n) so you can choose a reasonable size. Array and ListBuffer are also a mutable data types, which goes again your desire for functional style.

    Update: to get good performance out of the Sieve of Eratosthenes I think you'll need to store data in a native array, which also goes against your desire for style in functional programming. There might be a creative functional implementation though!

    Update: oops! Missed it! This approach works well too if you only divide by primes less than the square root of the number you are testing! I missed this, and unfortunately it's not easy to adjust my solution to do this because I'm storing the primes backwards.

    Update: here's a very non-functional solution that at least only checks up to the square root.

    rnative, you could use an Array, Vector or a ListBuffer to append the results. With the Array, however, you would need to estimate how much memory to allocate for it. Fortunately we know that pi(n) is about equal to n / ln(n) so you can choose a reasonable size. Array and ListBuffer are also a mutable data types, which goes again your desire for functional style.

    Update: to get good performance out of the Sieve of Eratosthenes I think you'll need to store data in a native array, which also goes against your desire for style in functional programming. There might be a creative functional implementation though!

    Update: oops! Missed it! This approach works well too if you only divide by primes less than the square root of the number you are testing! I missed this, and unfortunately it's not easy to adjust my solution to do this because I'm storing the primes backwards.

    Update: here's a very non-functional solution that at least only checks up to the square root.

    import scala.collection.mutable.ListBuffer
    
    def primesUnder(n: Int): List[Int] = {
      require(n >= 2)
    
      val primes = ListBuffer(2)
      for (i <- 3 to n) {
        if (prime(i, primes.iterator)) {
          primes += i
        }
      }
    
      primes.toList
    }
    
    // factors must be in sorted order
    def prime(num: Int, factors: Iterator[Int]): Boolean = 
      factors.takeWhile(_ <= math.sqrt(num).toInt) forall(num % _ != 0)
    

    Or I could use Vectors with my original approach. Vectors are probably not the best solution because they don't have the fasted O(1) even though it's amortized O(1).

    0 讨论(0)
  • 2020-12-01 22:59

    How about this.

    def getPrimeUnder(n: Int) = {
      require(n >= 2)
      val ol = 3 to n by 2 toList // oddList
      def pn(ol: List[Int], pl: List[Int]): List[Int] = ol match {
        case Nil => pl
        case _ if pl.exists(ol.head % _ == 0) => pn(ol.tail, pl)
        case _ => pn(ol.tail, ol.head :: pl)
      }
      pn(ol, List(2)).reverse
    }
    

    It's pretty fast for me, in my mac, to get all prime under 100k, its take around 2.5 sec.

    0 讨论(0)
提交回复
热议问题