我写了这段代码,以便在scala中找到小于给定数字i的素数。
def findPrime(i : Int) : List[Int] = i match {
case 2 => List(2)
case _ => {
val primeList = findPrime(i-1)
if(isPrime(i, primeList)) i :: primeList else primeList
}
}
def isPrime(num : Int, prePrimes : List[Int]) : Boolean = prePrimes.forall(num % _ != 0)
但是,我对findPrime函数有所了解,特别是这部分:
case _ => {
val primeList = findPrime(i-1)
if(isPrime(i, primeList)) i :: primeList else primeList
}
不是功能风格。
我还在学习函数式编程。任何人都可以帮我改进这些代码,使其更具功能性。
非常感谢。
答案 0 :(得分:18)
这是Sieve of Eratosthenes的功能实现,如Odersky's "Functional Programming Principles in Scala" Coursera课程中所示:
// Sieving integral numbers
def sieve(s: Stream[Int]): Stream[Int] = {
s.head #:: sieve(s.tail.filter(_ % s.head != 0))
}
// All primes as a lazy sequence
val primes = sieve(Stream.from(2))
// Dumping the first five primes
print(primes.take(5).toList) // List(2, 3, 5, 7, 11)
答案 1 :(得分:10)
这种风格对我来说很好看。尽管Eratosthenes筛选是一种非常有效的查找素数的方法,但您的方法也很有效,因为您只是针对已知素数进行分裂测试。但是你需要注意 - 你的递归函数不是尾递归的。尾递归函数不会修改递归调用的结果 - 在您的示例中,您将预先添加到递归调用的结果中。这意味着你将拥有一个长调用堆栈,因此findPrime不适用于大型i。这是一个尾递归解决方案。
def primesUnder(n: Int): List[Int] = {
require(n >= 2)
def rec(i: Int, primes: List[Int]): List[Int] = {
if (i >= n) primes
else if (prime(i, primes)) rec(i + 1, i :: primes)
else rec(i + 1, primes)
}
rec(2, List()).reverse
}
def prime(num: Int, factors: List[Int]): Boolean = factors.forall(num % _ != 0)
这个解决方案并不漂亮 - 让您的解决方案适用于大型参数更具细节性。由于列表是向后构建的以利用快速前置,因此列表需要反转。作为替代方案,您可以使用Array
,Vector
或ListBuffer
来附加结果。但是,使用Array
,您需要估计要为其分配多少内存。幸运的是,我们知道pi(n)
大约等于n / ln(n)
,因此您可以选择合理的尺寸。 Array
和ListBuffer
也是一种可变数据类型,这再次成为您对功能风格的渴望。
更新:为了从Eratosthenes的筛选中获得良好的性能,我认为您需要将数据存储在本机数组中,这也违背了您对函数式编程风格的需求。可能会有一个创造性的功能实现!
更新:哎呀!错过了!如果您只将质数除以小于您正在测试的数字的平方根,这种方法也很有效!我错过了这个,不幸的是,调整我的解决方案并不容易,因为我正在向后存储素数。
更新:这是一个非常缺乏功能的解决方案,至少只能检查平方根。
rnative,您可以使用Array
,Vector
或ListBuffer
来附加结果。但是,使用Array
,您需要估计要为其分配多少内存。幸运的是,我们知道pi(n)
大约等于n / ln(n)
,因此您可以选择合理的尺寸。 Array
和ListBuffer
也是一种可变数据类型,这再次成为您对功能风格的渴望。
更新:为了从Eratosthenes的筛选中获得良好的性能,我认为您需要将数据存储在本机数组中,这也违背了您对函数式编程风格的需求。可能会有一个创造性的功能实现!
更新:哎呀!错过了!如果您只将质数除以小于您正在测试的数字的平方根,这种方法也很有效!我错过了这个,不幸的是,调整我的解决方案并不容易,因为我正在向后存储素数。
更新:这是一个非常缺乏功能的解决方案,至少只能检查平方根。
import scala.collection.mutable.ListBuffer
def primesUnder(n: Int): List[Int] = {
require(n >= 2)
val primes = ListBuffer(2)
for (i <- 3 to n) {
if (prime(i, primes.iterator)) {
primes += i
}
}
primes.toList
}
// factors must be in sorted order
def prime(num: Int, factors: Iterator[Int]): Boolean =
factors.takeWhile(_ <= math.sqrt(num).toInt) forall(num % _ != 0)
或者我可以在原始方法中使用Vector
s。 Vector
可能不是最佳解决方案,因为它们没有禁食O(1),即使它已经摊销了O(1)。
答案 2 :(得分:6)
正如schmmd所提到的,你希望它是尾递归的,你也希望它是懒惰的。幸运的是,有一个完美的数据结构:Stream
。
这是一个非常有效的主要计算器,实现为Stream
,并进行了一些优化:
object Prime {
def is(i: Long): Boolean =
if (i == 2) true
else if ((i & 1) == 0) false // efficient div by 2
else prime(i)
def primes: Stream[Long] = 2 #:: prime3
private val prime3: Stream[Long] = {
@annotation.tailrec
def nextPrime(i: Long): Long =
if (prime(i)) i else nextPrime(i + 2) // tail
def next(i: Long): Stream[Long] =
i #:: next(nextPrime(i + 2))
3 #:: next(5)
}
// assumes not even, check evenness before calling - perf note: must pass partially applied >= method
def prime(i: Long): Boolean =
prime3 takeWhile (math.sqrt(i).>= _) forall { i % _ != 0 }
}
Prime.is
是主要检查谓词,Prime.primes
返回所有素数的Stream
。 prime3
是计算Stream的位置,使用素数谓词来检查小于i
的平方根的所有素数除数。
答案 3 :(得分:3)
/**
* @return Bitset p such that p(x) is true iff x is prime
*/
def sieveOfEratosthenes(n: Int) = {
val isPrime = mutable.BitSet(2 to n: _*)
for (p <- 2 to Math.sqrt(n) if isPrime(p)) {
isPrime --= p*p to n by p
}
isPrime.toImmutable
}
答案 4 :(得分:1)
筛选方法是小数字列表(最多1000万-100万左右)的最佳选择。 见:Sieve of Eratosthenes
即使您想要找到更大的数字,也可以使用此方法生成的列表作为除数来测试最多n ^ 2的数字,其中n是列表的限制。
答案 5 :(得分:1)
@mfa has mentioned using a Sieve of Eratosthenes - SoE和@Luigi Plinge提到应该使用功能代码完成,所以@netzwerg has posted a non-SoE version;在这里,我发布了一个&#34;几乎&#34;使用完全不可变状态的SoE的功能版本,除了I posted as an answer to another question:
的可变BitSet(可变性而非性能不可变)的内容object SoE {
def makeSoE_Primes(top: Int): Iterator[Int] = {
val topndx = (top - 3) / 2
val nonprms = new scala.collection.mutable.BitSet(topndx + 1)
def cullp(i: Int) = {
import scala.annotation.tailrec; val p = i + i + 3
@tailrec def cull(c: Int): Unit = if (c <= topndx) { nonprms += c; cull(c + p) }
cull((p * p - 3) >>> 1)
}
(0 to (Math.sqrt(top).toInt - 3) >>> 1).filterNot { nonprms }.foreach { cullp }
Iterator.single(2) ++ (0 to topndx).filterNot { nonprms }.map { i: Int => i + i + 3 }
}
}
答案 6 :(得分:1)
这个怎么样。
def getPrimeUnder(n: Int) = {
require(n >= 2)
val ol = 3 to n by 2 toList // oddList
def pn(ol: List[Int], pl: List[Int]): List[Int] = ol match {
case Nil => pl
case _ if pl.exists(ol.head % _ == 0) => pn(ol.tail, pl)
case _ => pn(ol.tail, ol.head :: pl)
}
pn(ol, List(2)).reverse
}
对我而言,在我的机器中,让所有素数低于10万,它的速度大约为2.5秒。
答案 7 :(得分:0)
标量fp方法
// returns the list of primes below `number`
def primes(number: Int): List[Int] = {
number match {
case a
if (a <= 3) => (1 to a).toList
case x => (1 to x - 1).filter(b => isPrime(b)).toList
}
}
// checks if a number is prime
def isPrime(number: Int): Boolean = {
number match {
case 1 => true
case x => Nil == {
2 to math.sqrt(number).toInt filter(y => x % y == 0)
}
}
}
答案 8 :(得分:0)
def primeNumber(range: Int): Unit ={
val primeNumbers: immutable.IndexedSeq[AnyVal] =
for (number :Int <- 2 to range) yield {
val isPrime = !Range(2, Math.sqrt(number).toInt).exists(x => number % x == 0)
if(isPrime) number
}
for(prime <- primeNumbers) println(prime)
}
答案 9 :(得分:-1)
object Primes {
private lazy val notDivisibleBy2: Stream[Long] = 3L #:: notDivisibleBy2.map(_ + 2)
private lazy val notDivisibleBy2Or3: Stream[Long] = notDivisibleBy2
.grouped(3)
.map(_.slice(1, 3))
.flatten
.toStream
private lazy val notDivisibleBy2Or3Or5: Stream[Long] = notDivisibleBy2Or3
.grouped(10)
.map { g =>
g.slice(1, 7) ++ g.slice(8, 10)
}
.flatten
.toStream
lazy val primes: Stream[Long] = 2L #::
notDivisibleBy2.head #::
notDivisibleBy2Or3.head #::
notDivisibleBy2Or3Or5.filter { i =>
i < 49 || primes.takeWhile(_ <= Math.sqrt(i).toLong).forall(i % _ != 0)
}
def apply(n: Long): Stream[Long] = primes.takeWhile(_ <= n)
def getPrimeUnder(n: Long): Long = Primes(n).last
}