【问题标题】:Questions on Scala performance tunningScala 性能调优问题
【发布时间】:2014-10-31 09:17:39
【问题描述】:

我是 Scala 新手,刚刚写了这个程序:

def isPrime(num: Int, primes: scala.collection.mutable.MutableList[Int]): Boolean = {
  primes.takeWhile( i =>  i * i <= num).forall( num % _ > 0)
}
//def isPrime(num: Int, primes: scala.collection.mutable.MutableList[Int]): Boolean = {
//  !primes.forall(num%_!=0)
//}


def primeListUnder(upper: Int) = { 
  val primes=scala.collection.mutable.MutableList(2,3); 
  var num=4
  while(num<upper) {
    if(isPrime(num,primes))
    { 
      primes+=num
    }
    num+=1
  }
  primes
}

这是试图获取某个指定上限下的所有素数。但它运行得非常慢。 有什么建议吗?

添加: 其实我的意思是要知道为什么这个程序运行得这么慢。这不是关于如何计算素数。

编辑: 将 isPrime 方法更改为首先过滤掉一些数字(数学上)。 现在运行速度更快,在我的 mac 上大约需要 10 秒才能数到 2000000;

【问题讨论】:

  • 您说它运行“非常慢”的依据是什么?有更快的方法,但你的方法似乎不是不合理的方法
  • 花 10 秒来找到 200 万个素数是很慢的,因为即使使用简单的 Eratosthenes 基本筛,这样一项微不足道的任务也只需要 10 毫秒。部分原因是您的模(“%”)运算所暗示的除法是现代 CPU 以每 10 个 CPU 时钟周期执行的最慢的原始整数运算之一,而加法通常需要 CPU 时钟周期的一小部分,并且甚至乘法只需要大约一个时钟周期。 Eratosthenes 筛子使用简单的基本操作来执行所有内部循环操作,每个循环只需几个时钟。

标签: performance scala


【解决方案1】:

要列出所有小于特定数字的素数,您应该使用埃拉托色尼筛法。 它比你的算法快得多。

您的算法太慢了,因为它会检查 每个all 质数小于它的数,直到找到它的除数。因此,每个数字都将使用至少一个质数进行检查。 但是当检查的数量增加时,素数列表会增加,可能的检查数量也会增加。

多次检查后会有号码被拒绝。 比如 26 号在检查 2,3,5,7,11,13 后会被拒绝。

此外,在检查所有次质数后,每个质数都将被接受。

与您的算法相比,埃拉托色尼筛算法将“触摸”每个数字,将其标记为“素数”或“非素数”。

【讨论】:

  • 谢谢!但据我了解,“forall”方法一旦不符合预测就会退出,对吧?然后“primes.forall”将在“num%_==0”的第一个数字上退出
  • @StanleyShi,虽然您是正确的,您的程序只会检查直到找到一个均分的除数,但找到的每个素数都需要每个已找到的素数对其进行测试(试除法)它的平方根。因此,平均而言,每个找到的素数所做的工作量增加得很快,检查素数的数字范围越大。这与之前提到的其他算法(例如埃拉托色尼筛法)形成对比,其中每个找到的素数的工作量随着范围的增加而非常缓慢地增加。
【解决方案2】:

正如@rtruszk 所提到的,从my answer on the other linked question 转发的以下代码用于真正的埃拉托色尼筛 (SoE) 将比您的代码在更大范围内运行得快得多:

object SoE {
  def makeSoE_Primes(top: Int): Iterator[Int] = {
    val topndx = (top - 3) / 2
    val nonprms = new scala.collection.mutable.BitSet(topndx + 1)
    def cullp(i: Int) = {
      import scala.annotation.tailrec; val p = i + i + 3
      @tailrec def cull(c: Int): Unit = if (c <= topndx) { nonprms += c; cull(c + p) }
      cull((p * p - 3) >>> 1)
    }
    (0 to (Math.sqrt(top).toInt - 3) >>> 1).filterNot { nonprms }.foreach { cullp }
    Iterator.single(2) ++ (0 to topndx).filterNot { nonprms }.map { i: Int => i + i + 3 }
  }
}

上面的代码主要是函数形式(使用尾递归或高阶函数以及不变性,而不是用于快速筛选复合数的 BitSet 数组的内容)。它不如使用 Java.util.BitSet 快,但生成的代码稍微优雅一些​​。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2011-06-21
    • 2016-05-17
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-10-21
    相关资源
    最近更新 更多