Стиль выглядит хорошо для меня. Хотя решето Эратосфена — очень эффективный способ нахождения простых чисел, ваш подход тоже работает хорошо, поскольку вы проверяете деление только на известные простые числа. Однако вам нужно следить за тем, чтобы ваша рекурсивная функция не была хвостовой рекурсией. Хвостовая рекурсивная функция не изменяет результат рекурсивного вызова - в вашем примере вы добавляете результат к результату рекурсивного вызова. Это означает, что у вас будет длинный стек вызовов, поэтому 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
также являются изменяемыми типами данных, что опять-таки соответствует вашему стремлению к функциональному стилю.
Обновление: чтобы получить хорошую производительность от решета Эратосфена, я думаю, вам нужно хранить данные в собственном массиве, что также противоречит вашему стремлению к стилю функционального программирования. Однако может быть творческая функциональная реализация!
Обновление: ой! Пропустил его! Этот подход также хорошо работает, если вы делите только на простые числа, меньшие квадратного корня из числа, которое вы тестируете! Я пропустил это, и, к сожалению, нелегко настроить мое решение для этого, потому что я храню простые числа в обратном порядке.
Обновление: вот очень нефункциональное решение, которое, по крайней мере, проверяет только квадратный корень.
Обычно вы можете использовать Array
, Vector
или ListBuffer
для добавления результатов. Однако с Array
вам нужно будет оценить, сколько памяти выделить для него. К счастью, мы знаем, что pi(n)
примерно равно n / ln(n)
, поэтому вы можете выбрать разумный размер. Array
и ListBuffer
также являются изменяемыми типами данных, что опять-таки соответствует вашему стремлению к функциональному стилю.
Обновление: чтобы получить хорошую производительность от решета Эратосфена, я думаю, вам нужно хранить данные в собственном массиве, что также противоречит вашему стремлению к стилю функционального программирования. Однако может быть творческая функциональная реализация!
Обновление: ой! Пропустил его! Этот подход также хорошо работает, если вы делите только на простые числа, меньшие квадратного корня из числа, которое вы тестируете! Я пропустил это, и, к сожалению, нелегко настроить мое решение для этого, потому что я храню простые числа в обратном порядке.
Обновление: вот очень нефункциональное решение, которое, по крайней мере, проверяет только квадратный корень.
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
s, вероятно, не лучшее решение, потому что у них нет поста O (1), даже если он амортизирован O (1).
person
schmmd
schedule
14.03.2012