performance - 为什么我的欧拉计划第 12 题算法这么慢?

标签 performance algorithm scala

我已经在 Scala 中为 PE P12 创建了解决方案,但速度非常非常慢。有人可以告诉我为什么吗?如何优化这个? calculateDevisors() - 朴素的方法和 calculateNumberOfDivisors() - 除数函数具有相同的速度:/

import annotation.tailrec

def isPrime(number: Int): Boolean = {
  if (number < 2 || (number != 2 && number % 2 == 0) || (number != 3 && number % 3 == 0))
    false
  else {
    val sqrtOfNumber = math.sqrt(number) toInt

    @tailrec def isPrimeInternal(divisor: Int, increment: Int): Boolean = {
      if (divisor > sqrtOfNumber)
        true
      else if (number % divisor == 0)
        false
      else
        isPrimeInternal(divisor + increment, 6 - increment)
    }

    isPrimeInternal(5, 2)
  }
}

def generatePrimeNumbers(count: Int): List[Int] = {
  @tailrec def generatePrimeNumbersInternal(number: Int = 3, index: Int = 0,
                                            primeNumbers: List[Int] = List(2)): List[Int] = {
    if (index == count)
      primeNumbers
    else if (isPrime(number))
      generatePrimeNumbersInternal(number + 2, index + 1, primeNumbers :+ number)
    else
      generatePrimeNumbersInternal(number + 2, index, primeNumbers)
  }

  generatePrimeNumbersInternal();
}

val primes = Stream.cons(2, Stream.from(3, 2) filter {isPrime(_)})

def calculateDivisors(number: Int) = {
  for {
    divisor &lt;- 1 to number
    if (number % divisor == 0)
  } yield divisor
}

@inline def decomposeToPrimeNumbers(number: Int) = {
  val sqrtOfNumber = math.sqrt(number).toInt

  @tailrec def decomposeToPrimeNumbersInternal(number: Int, primeNumberIndex: Int = 0,
                                               factors: List[Int] = List.empty[Int]): List[Int] = {
    val primeNumber = primes(primeNumberIndex)

    if (primeNumberIndex > sqrtOfNumber)
      factors
    else if (number % primeNumber == 0)
      decomposeToPrimeNumbersInternal(number / primeNumber, primeNumberIndex, factors :+ primeNumber)
    else
      decomposeToPrimeNumbersInternal(number, primeNumberIndex + 1, factors)
  }

  decomposeToPrimeNumbersInternal(number) groupBy {n => n} map {case (n: Int, l: List[Int]) => (n, l size)}
}

@inline def calculateNumberOfDivisors(number: Int) = {
  decomposeToPrimeNumbers(number) map {case (primeNumber, exponent) => exponent + 1} product
}

@tailrec def calculate(number: Int = 12300): Int = {
  val triangleNumber = ((number * number) + number) / 2
  val startTime = System.currentTimeMillis()
  val numberOfDivisors = calculateNumberOfDivisors(triangleNumber)
  val elapsedTime = System.currentTimeMillis() - startTime

  printf("%d: V: %d D: %d T: %dms\n", number, triangleNumber, numberOfDivisors, elapsedTime)

  if (numberOfDivisors > 500)
    triangleNumber
  else
    calculate(number + 1)
}

println(calculate())

最佳答案

您可以先检查什么 慢。例如,您的素数计算非常非常慢。对于每个数字 n,您尝试将 n 除以从 5 到 sqrt(n) 的每个数字,跳过 2 和 3 的倍数。你不仅不会跳过你已经知道不是素数的数字,而且即使你解决了这个问题,这个算法的复杂度也是much worse。比传统的埃拉托色尼筛法。查看 Sieve 的一个 Scala 实现 here .

这并不是说您的其余代码也不是最理想的,但我会把它留给其他人。

编辑

确实,对 Stream 的索引访问很糟糕。这是与 Stream 一起使用的重写,而不是将所有内容都转换为 Array。另外,请注意第一个 if 之前的注释,以了解代码中可能存在的错误。

  @tailrec def decomposeToPrimeNumbersInternal(number: Int, primes: Stream[Int],
                                               factors: List[Int] = List.empty[Int]): List[Int] = {
    val primeNumber = primes.head

    // Comparing primeNumberIndex with sqrtOfNumber didn't make any sense
    if (primeNumber > sqrtOfNumber) 
      factors
    else if (number % primeNumber == 0)
      decomposeToPrimeNumbersInternal(number / primeNumber, primes, factors :+ primeNumber)
    else
      decomposeToPrimeNumbersInternal(number, primes.tail, factors)
  }

关于performance - 为什么我的欧拉计划第 12 题算法这么慢?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/3737637/

相关文章:

java - Cassandra 绑定(bind)语句和内存泄漏

c++ - 如何在不污染缓存的情况下读取大量数据?

performance - 为什么类型提示不能提高这个函数的性能?

sql-server - 聚集索引和非聚集索引实际上意味着什么?

c++ - 理解第 nth_element

algorithm - 无向图中的前向边

java - 根据具有相同名称的特定字段的权重调整 Lucene 搜索结果分数

r - 加速 R 算法计算 Hellinger 距离的距离矩阵

algorithm - Big O 的复杂性可以有不止一个答案吗?

scala - 在没有 Spark 的 Scala 中创建 Parquet 文件