我正在尝试在Scala中实现Sieve of Eratosthenes。
我首先初始化所有奇数加2的序列:
// (end goal is to find all prime factors of bigNumber)
val largestPrime : Long = Math.ceil(Math.sqrt(bigNumber)).toLong
var nums : Seq[Long] = (3L to largestPrime by 2L).toSeq
nums +: 2L
现在nums
包含Seq(2,3,5,7,9,11,13,15,...,(largestPrime))。然后,通过Sieve,我想迭代每个元素,并从Seq中过滤掉该元素的所有倍数。它看起来像这样,除了这只是迭代每个奇数:
for(i : Long <- 3L to largestPrime by 2L) {
nums = nums.filter((j : Long) => j == i || j % i != 0)
}
相反,我想要使用这样的东西:
for(i <- nums) {
// filter
}
但是,当然,这只是简单地将序列复制到迭代器中,然后迭代nums
中的每个值,就像它在for循环的开头一样(所以在这种情况下,它完全等同于前面的例子) )。我想要它,每次迭代,从nums
获取下一个值。
实现这个的最佳方法是什么?我应该使用索引变量和while循环吗?我不确定如何从序列中获取元素(即如何获取序列的元素x,其中x是索引)。或者有更实用的方法吗?
编辑:我刚刚找到scanLeft
函数,我正试图掌握如何使用它,因为我怀疑它可能在这种情况下有用......
答案 0 :(得分:4)
让我们从我认为最重要的问题开始吧。你有这个:
for (i <- mi) { mi = something else }
此不会更改正在迭代的mi
。 mi
始终保持不变。可能您可以变异 mi
的值,但更改它将无效。顺便说一下,改变它也可能不起作用。
那么,你是怎么做到的?你不用于理解 - 或者至少不是这种方式。您可以查看我自己的版本here,它会迭代不同于正在变异的集合。或者这是一个单行:
(n: Int) => (2 to n) |> (r => r.foldLeft(r.toSet)((ps, x) => if (ps(x)) ps -- (x * x to n by x) else ps))
现在,回到你想要做什么......当你使用for-comprehension时,你实际上是在调用方法foreach
,map
或{{ 1}},所以你需要一个能够处理这些方法之一的集合和没有“下一个”元素从一次迭代变为下一次迭代的麻烦。正如我所说,我不确定Scala的任何系列是否合适。如果你这样做,你最好使用flatMap
循环并自己跟踪事物。例如:
while
请注意,我会指向def primes(n: Int) = {
import scala.collection.mutable.LinkedList
val primes = LinkedList(3 to n by 2: _*)
var p = primes
while (p.nonEmpty) {
var scanner = p
while (scanner.next.nonEmpty) {
if (scanner.next.head % p.head == 0)
scanner.next = scanner.next.next
else
scanner = scanner.next
}
p = p.next
}
primes
}
的开头,通过每个已知素数移动LinkedList
,并将p
移动到所有剩余数字以剪切非素数。< / p>
答案 1 :(得分:2)
scala.collection.immutable.Stream上的文档中的示例是筛子:
object Main extends Application {
def from(n: Int): Stream[Int] =
Stream.cons(n, from(n + 1))
def sieve(s: Stream[Int]): Stream[Int] =
Stream.cons(s.head, sieve(s.tail filter { _ % s.head != 0 }))
def primes = sieve(from(2))
primes take 10 print
}
答案 2 :(得分:0)
既不是一个好的功能解决方案,也不是一个能够揭示模糊Scala图书馆宝藏的解决方案,但是自己很容易构建一个专门的迭代器。
class ModifyingIterator(var collection: Seq[Long]) extends Iterator[Long] {
var current = collection.head
def next = {
current = collection.find(_ > current).get
current
}
def hasNext = collection.exists(_ > current)
}
val mi = new ModifyingIterator(nums)
for (i <- mi) {
mi.collection = mi.collection.filter((j : Long) => j == i || j % i != 0)
}
println(mi.collection)
ModifyingIterator
跟踪当前项目并允许重新分配用于迭代的集合。下一个项目总是大于当前项目。
当然,应该使用更好的数据结构,它不会跟踪当前的值但保留指向当前项的指针以便摆脱每次无用的搜索。
答案 3 :(得分:0)
有一篇有趣的论文:http://www.cs.hmc.edu/~oneill/papers/Sieve-JFP.pdf
我试图将该论文中给出的Haskell代码翻译成Scala,但我没有测试性能。
object primes {
type SI = Stream[Int]
def sieve:SI = {
def wheel2357:SI = Stream(4,2,4,6,2,6,4,2,4,6,6,
2,6,4,2,6,4,6,8,4,2,4,2,4,8,6,4,6,2,4,6,2,6,
6,4,2,4,6,2,6,4,2,4,2,10,2,10,2) append wheel2357
def spin(s:SI, n:Int):SI = Stream.cons(n, spin(s.tail, n + s.head))
case class It(value:Int, step:Int) {
def next = new It(value + step, step)
def atLeast(c:Int):It =
if (value >= c) this
else new It(value + step, step).atLeast(c)
}
implicit object ItOrdering extends Ordering[It] {
def compare(thiz:It, that:It) = {
val r = thiz.value - that.value
if (r == 0) thiz.step - that.step else r
}
}
import scala.collection.immutable.TreeSet
def sieve(cand:SI, set:Set[It]):SI = {
val c = cand.head
val set1 = TreeSet[It]() ++ set.dropWhile(_.value < c) ++
set.takeWhile(_.value < c).map(_.atLeast(c))
if (set1.elements.next.value == c) {
val set2 = TreeSet[It]() ++ set1.dropWhile(_.value == c) ++
set1.takeWhile(_.value == c).map(_.next)
sieve(cand.tail, set2)
} else {
Stream.cons(c, sieve(cand.tail, set1 + It(c*c,2*c)))
}
}
Stream(2,3,5,7,11) append sieve(spin(wheel2357,13),
new TreeSet[It] + It(121,22))
}
def main(args:Array[String]) {
sieve.takeWhile(_ < 1000).foreach(println)
}
}