我使用Stream和lazy val编写了以下懒惰筛选算法的实现:
def primes(): Stream[Int] = {
lazy val ps = 2 #:: sieve(3)
def sieve(p: Int): Stream[Int] = {
p #:: sieve(
Stream.from(p + 2, 2).
find(i=> ps.takeWhile(j => j * j <= i).
forall(i % _ > 0)).get)
}
ps
}
以及使用(mutable)ListBuffer的以下实现:
import scala.collection.mutable.ListBuffer
def primes(): Stream[Int] = {
def sieve(p: Int, ps: ListBuffer[Int]): Stream[Int] = {
p #:: { val nextprime =
Stream.from(p + 2, 2).
find(i=> ps.takeWhile(j => j * j <= i).
forall(i % _ > 0)).get
sieve(nextprime, ps += nextprime)
}
}
sieve(3, ListBuffer(3))}
当我做primes()。takeWhile(_&lt; 1000000).size时,第一个实现比第二个实现快3倍。 对此有何解释?
我编辑了第二个版本:它应该是筛子(3,ListBuffer(3))而不是筛子(3,ListBuffer())。
答案 0 :(得分:6)
嗯,我的猜测是这一行:
find(i=> ps.takeWhile(j => j * j <= i).forall(i % _ > 0)).get
在ListBuffer
上,takeWhile
创建一个临时集合(不断变大和变大)。同时,Stream
由于其不严格,因此避免这样做。一旦forall
失败,它就会停止计算takeWhile
。
答案 1 :(得分:2)
并没有真正回答这个问题,但是因为我花了一些时间对各种组合进行基准测试......
如果在内部循环中使用Iterator
,ArrayBuffer
并避免takeWhile
,则可以获得更好的性能,以最大限度地减少内存分配。
def primes2(): Stream[Int] = {
def sieve(p: Int, ps: ArrayBuffer[Int]): Stream[Int] = {
def hasNoDivisor(prime_? :Int, j: Int = 0): Boolean = {
val n = ps(j)
if (n*n > prime_?) true
else if (prime_? % n == 0) false else hasNoDivisor(prime_?, j+1)
}
p #:: {
val nextprime = Iterator.from(ps.last + 2, 2).find(hasNoDivisor(_)).get
sieve(nextprime, ps += nextprime)
}
}
sieve(3, ArrayBuffer(3))
}
这是一个Iterator
而不是Stream
的版本,速度更快,如果需要,您可以随时使用primes3().toStream
获取流。
def primes3() = List(2,3).iterator ++ new Iterator[Int] {
val ps = ArrayBuffer[Int](3)
def hasNoDivisor(prime_? :Int, j: Int = 0): Boolean = {
val n = ps(j)
if (n*n > prime_?) true
else if (prime_? % n == 0) false else hasNoDivisor(prime_?, j+1)
}
def hasNext = true
def next() = {
val nextprime = Iterator.from(ps.last + 2, 2).find(hasNoDivisor(_)).get
ps += nextprime
nextprime
}
}
结果:
primes : warming...
primes : running...
primes : elapsed: 3.711
res39: Int = 283145
primes2: warming...
primes2: running...
primes2: elapsed: 1.039
res40: Int = 283145
primes3: warming...
primes3: running...
primes3: elapsed: 0.530
res41: Int = 283146
我还尝试将from
,find
和hasNoDivisor
替换为几个while
循环,这样更快,但不太容易理解。