代码之家  ›  专栏  ›  技术社区  ›  nishu

工程Euler 7 Scala问题

  •  2
  • nishu  · 技术社区  · 15 年前

    我试着用scala 2.8解决project euler问题7

    我实现的第一个解决方案需要大约8秒

    def problem_7:Int = {
        var num = 17;
        var primes = new ArrayBuffer[Int]();
        primes += 2
        primes += 3
        primes += 5
        primes += 7
        primes += 11
        primes += 13
    
        while (primes.size < 10001){
    
            if (isPrime(num, primes)) primes += num
            if (isPrime(num+2, primes)) primes += num+2
    
            num += 6
        }
        return primes.last;
    }
    
    def isPrime(num:Int, primes:ArrayBuffer[Int]):Boolean = {
        // if n == 2 return false;
        // if n == 3 return false;
        var r = Math.sqrt(num)  
        for (i <- primes){
            if(i <= r ){
                if (num % i == 0) return false;
            }
        }
        return true;
    }
    

    后来我尝试了同样的问题,但没有在数组缓冲区中存储质数。这需要118秒。

    def problem_7_alt:Int = {
        var limit = 10001;
        var count = 6;
        var num:Int = 17;
    
        while(count < limit){
    
            if (isPrime2(num)) count += 1;
            if (isPrime2(num+2)) count += 1;
    
            num += 6;
        }
        return num;
    }
    
    def isPrime2(n:Int):Boolean = {
        // if n == 2 return false;
        // if n == 3 return false;
    
        var r = Math.sqrt(n)
        var f = 5;
        while (f <= r){
            if (n % f == 0) {
                return false;
            } else if (n % (f+2) == 0) {
                return false;
            }
                f += 6;
        }
        return true;
    }
    

    我尝试在scala中使用各种可变数组/列表实现,但无法使解决方案更快。我不认为将int存储在10001大小的数组中会使程序变慢。在scala中使用列表/数组有更好的方法吗?

    4 回复  |  直到 10 年前
        1
  •  2
  •   Rex Kerr    15 年前

    如果使用正确的算法,使用array应该可以使它在大约0秒内工作。例如,在我的系统上,这大约需要7毫秒:

    class Primes(bufsize: Int) {
      var n = 1
      val pbuf = new Array[Int](bufsize max 1)
      pbuf(0) = 2
      def isPrime(num: Int): Boolean = {
        var i = 0
        while (i < n && pbuf(i)*pbuf(i) <= num) {
          if (num % pbuf(i) == 0) return false
          i += 1
        }
        if (pbuf(i)*pbuf(i) < num) {
          i = pbuf(i)
          while (i*i <= num) {
            if (num % i == 0) return false
            i += 2
          }
        }
        return true;
      }
      def fillBuf {
        var i = 3
        n = 1
        while (n < bufsize) {
          if (isPrime(i)) { pbuf(n) = i; n += 1 }
          i += 2
        }
      }
      def lastPrime = { if (n<bufsize) fillBuf ; pbuf(pbuf.length-1) }
    }
    object Primes {
      def timedGet(num: Int) = {
        val t0 = System.nanoTime
        val p = (new Primes(num)).lastPrime
        val t1 = System.nanoTime
        (p , (t1-t0)*1e-9)
      }
    }
    

    结果(在第二次调用时;第一次有一些开销):

    scala> Primes.timedGet(10001)
    res1: (Int, Double) = (104743,0.00683394)
    
        2
  •  5
  •   Daniel C. Sobral    15 年前

    问题是 ArrayBuffer 是参数化的,所以它真正存储的是对 Object . 任何提及 Int 根据需要自动装箱和拆箱,这使得它非常慢。Scala 2.7的速度非常慢,它使用Java原语来执行,这非常缓慢。scala 2.8采用了另一种方法,使其更快。但任何拳击/开膛动作都会让你慢下来。此外,您首先要查找 小精灵 在堆里,然后再抬头寻找 java.lang.Integer 包含 int --两次内存访问,这使得它比其他解决方案慢得多。

    当scala集合变得专门化时,应该会更快。我不知道这是否足以打败你的第二个版本。

    现在,你能做的就是利用 Array 相反。因为Java 数组 不清除,避免装箱/拆箱。

    此外,当您用于理解时,您的代码有效地存储在为每个元素调用的方法中。因此,您还进行了许多方法调用,这是速度较慢的另一个原因。唉,有人为scala编写了一个插件,它至少优化了一个实例以避免出现这种情况。

        3
  •  1
  •   Khaled Alshaya    15 年前

    我认为你必须跳出框框思考:)

    因为问题是可以处理的,所以可以使用 Sieve of Eratosthenes 有效地解决它。

        4
  •  1
  •   ams    15 年前

    这是一个递归的解决方案(使用第一个解决方案中的isprime函数)。选择不变性(即尽量不使用 var 所以我在这里做过(事实上没有 var S或 val S!)。不过,我这里没有scala安装,所以不知道这是否真的更快!

    def problem_7:Int = { 
      def isPrime_(n: Int) = (n % 6 == 1 || n % 6 == 5) && isPrime(n)
      def process(n: Int, acc: List[Int]): Int = {
        if (acc.size == 10001) acc.head
        else process(n+1, if isPrime_(n) n :: acc else acc) 
      }
      process(1, Nil)
    }