项目 Euler 7 Scala 问题

发布于 2024-08-25 04:31:19 字数 1499 浏览 10 评论 0原文

我试图使用 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;
}

后来我尝试了同样的问题,但没有在数组缓冲区中存储素数。这需要 0.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 中使用列表/数组?

I was trying to solve Project Euler problem number 7 using scala 2.8

First solution implemented by me takes ~8 seconds

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;
}

Later I tried the same problem without storing prime numbers in array buffer. This take .118 seconds.

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;
}

I tried using various mutable array/list implementations in Scala but was not able to make solution one faster. I do not think that storing Int in a array of size 10001 can make program slow. Is there some better way to use lists/arrays in scala?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(4

×眷恋的温暖 2024-09-01 04:31:19

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

当 Scala 集合变得专业化时,速度应该会快很多。我不知道它是否足以击败你的第二个版本。

现在,您可以使用Array来解决这个问题。因为 Java 的 Array 不会被擦除,所以您可以避免装箱/拆箱。

此外,当您使用 for 推导式时,您的代码实际上存储在为每个元素调用的方法中。因此,您还进行了许多方法调用,这是速度较慢的另一个原因。唉,有人为 Scala 编写了一个插件,它优化了至少一种 for 推导式以避免这种情况。

The problem here is that ArrayBuffer is parameterized, so what it really stores are references to Object. Any reference to an Int is automatically boxed and unboxed as needed, which makes it very slow. It is incredibly slow with Scala 2.7, which uses a Java primitive to do that, which does it very slowly. Scala 2.8 takes another approach, making it faster. But any boxing/unboxing will slow you down. Furthermore, you are first looking up the ArrayBuffer in the heap, and then looking up again for java.lang.Integer containing the Int -- two memory accesses, which makes it way slower than your other solution.

When Scala collections become specialized, it should be plenty faster. Whether it should be enough to beat your second version or not, I don't know.

Now, what you may do to get around that is to use Array instead. Because Java's Array are not erased, you avoid the boxing/unboxing.

Also, when you use for-comprehensions, your code is effectively stored in a method which is called for each element. So you are also making many method calls, which is another reason this is slower. Alas, someone wrote a plugin for Scala which optimizes at least one case of for-comprehensions to avoid that.

萌化 2024-09-01 04:31:19

使用 Array 应该可以通过正确的算法在大约零秒内完成。例如,在我的系统上,这大约需要 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)

Using Array should make it work in about zero seconds with the right algorithm. This, for example, takes about 7 milliseconds on my system:

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)
  }
}

Result (on second call; first has some overhead):

scala> Primes.timedGet(10001)
res1: (Int, Double) = (104743,0.00683394)
埋情葬爱 2024-09-01 04:31:19

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

因为问题是可以管理的,你可以使用 埃拉托斯特尼筛法 非常有效地解决它。

I think you have to think out of the box :)

Because the problem is manageable, you can use Sieve of Eratosthenes to solve it very efficiently.

萌辣 2024-09-01 04:31:19

这是一个递归解决方案(使用第一个解决方案中的 isPrime 函数)。喜欢不变性(即尽量不使用 var )似乎是很好的 Scala 风格,所以我在这里这样做了(事实上没有 var 或val!)。我这里没有安装 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)
}

Here's a recursive solution (using the isPrime function from your first solution). It seems to be good Scala style to prefer immutability (i.e. to try not to use vars) so I've done that here (in fact there are no vars or vals!). I don't have a Scala installation here though so can't tell if this is actually any quicker!

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)
}
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文