我试图使用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中使用列表/数组?
答案 0 :(得分:5)
这里的问题是ArrayBuffer
是参数化的,所以它真正存储的是对Object
的引用。对Int
的任何引用都会根据需要自动装箱和取消装箱,这使得它非常慢。使用Scala 2.7的速度非常慢,它使用Java原语来做到这一点,这非常缓慢。 Scala 2.8采用了另一种方法,使其更快。但任何拳击/拆箱都会让你失望。此外,您首先在堆中查找ArrayBuffer
,然后再次查找包含java.lang.Integer
的{{1}} - 两次内存访问,这使得它比您的其他解决方案慢
当Scala集合变得专业化时,它应该更快。是否应该足以击败你的第二个版本,我不知道。
现在,您可以采取的措施是使用Int
代替。因为Java的Array
没有被删除,所以你可以避免装箱/拆箱。
此外,当您使用for-comprehensions时,您的代码将有效地存储在为每个元素调用的方法中。所以你也在进行很多方法调用,这是另一个原因,这是另一个原因。唉,有人为Scala编写了一个插件,该插件优化了至少一个for-comprehension案例,以避免这种情况。
答案 1 :(得分:2)
使用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)
答案 2 :(得分:1)
我认为你必须开箱即用:)
由于问题可以解决,您可以使用Sieve of Eratosthenes非常有效地解决问题。
答案 3 :(得分:1)
这是一个递归解决方案(使用第一个解决方案中的isPrime函数)。喜欢不变性似乎是不错的Scala风格(即尽量不使用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)
}