我在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 <- 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())
答案 0 :(得分:2)
您可以先检查 的速度是多少。例如,您的主要计算非常非常缓慢。对于每个号码n
,您尝试将n
除以每个数字从5到sqrt(n)
,跳过2和3的倍数。不仅不会跳过已知的数字而不是素数,但即使你解决了这个问题,这个算法的复杂性比传统的Eratosthenes筛子还要much worse。请参阅Sieve here的一个Scala实现。
这并不是说你的其余代码也不是次优,但我会把它留给别人。
修改强>
确实,对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)
}
答案 1 :(得分:1)
相比......慢?你怎么知道这是Scala的一个问题,而不是你的算法?
无可否认,快速阅读代码表明您可能会一遍又一遍地重新计算素数和其他值。 isPrimeInternal
跳出来作为可能出现问题的可能情况。
答案 2 :(得分:0)
你的代码不可编辑,有些部分缺失,所以我猜这里。经常伤害性能的一些事情是在集合中发生装箱/拆箱。我注意到的另一件事是你将你的素数作为一个流 - 这是一件好事 - 但是不要在你的isPrime函数中利用它,它使用一个原始的2,3轮(1和5 mod 6)代替。我可能错了,但试着用
替换它def isPrime(number: Int): Boolean = {
val sq = math.sqrt(number + 0.5).toInt
! primes.takeWhile(_ <= sq).exists(p => number % p == 0)
}
答案 3 :(得分:0)
我的scala算法,用于计算给定数字的除数。它在解决方案中运行良好 项目欧拉问题12。
def countDivisors(numberToFindDivisor:BigInt):Int = {
def countWithAcc(numberToFindDivisor: BigInt, currentCandidate: Int, currentCountOfDivisors: Int,limit: BigInt): Int = {
if (currentCandidate >= limit) currentCountOfDivisors
else {
if (numberToFindDivisor % currentCandidate == 0)
countWithAcc(numberToFindDivisor, currentCandidate + 1, currentCountOfDivisors + 2, numberToFindDivisor / currentCandidate)
else
countWithAcc(numberToFindDivisor, currentCandidate + 1, currentCountOfDivisors, limit)
}
}
countWithAcc(numberToFindDivisor, 1, 0, numberToFindDivisor + 1)
}
答案 4 :(得分:-2)
def calculateDivisors(n: Int) = {
var res = 1
val intSqrt = Math.sqrt(n).toInt
for (i <- 2 until intSqrt) {
if (n % i == 0) {
res += 2
}
}
if (n == intSqrt * intSqrt) {
res += 1
}
res
}