Да, это , потому что вы "увеличиваете стек функций, вызываемых для получения следующего элемента, на единицу после каждой" итерации "" - т.е. добавлять новый фильтр поверх стека фильтров каждый раз после получения каждого простого числа. Это слишком много фильтров .
Это означает, что каждое произведенное простое число проверяется всеми предшествующими простыми числами - но на самом деле нужны только те, которые находятся ниже его квадратного корня. Например, чтобы получить 10001-е простое число, 104743
, , во время выполнения будет создано 10000 фильтров, созданных . Но есть только 66 простых чисел ниже 323
, квадратный корень из 104743
, поэтому только 66 фильтров были действительно необходимы . Все остальные 9934 будут там без нужды, занимая память, усердно работая и не принося никакой пользы.
Этот является ключевым недостатком этого "функционального сита", которое, как представляется, возникло в коде 1970-х годов Дэвидом Тернером, а позже нашло свой путь в книга SICP и другие места. Это , а не , что это пробное деление сито (а не сито Эратосфена ). Это слишком далеко беспокойство об этом. При оптимальном внедрении пробное деление способно очень быстро произвести 10000-е простое число.
Ключевой недостаток этого кода в том, что он не откладывает создание фильтров до нужного момента и в итоге создает слишком много из них.
Говоря сложности сейчас, код "старого сита": O (n 2 ) , в n
простых числах . Оптимальное пробное деление составляет O (n 1,5 / log 0,5 (n)) , а сито с эратосфенами составляет O (n * log ( п) * журнал (журнал (п))) . В качестве эмпирических порядков роста первое обычно обозначается как ~ n^2
, второе - ~ n^1.45
, а третье ~ n^1.2
.
В этом ответе вы найдете код, основанный на генераторах Python для оптимального пробного деления (2-я половина) . Это было , первоначально обсуждавшееся здесь , имеющее отношение к эквиваленту Haskell вашей функции сита.
В качестве иллюстрации «читаемый псевдокод» :) для старого сита -
primes = sieve [2..] where
sieve (x:xs) = x : sieve [ y | y <- xs, rem y x > 0 ]
-- list of 'y's, drawn from 'xs',
-- such that (y % x > 0)
и для оптимального пробного деления (TD) сита, синхронизированного по квадратам простых чисел,
primes = sieve [2..] primes where
sieve (x:xs) ps = x : (h ++ sieve [ y | y <- t, rem y p > 0 ] qs)
where
(p:qs) = ps -- 'p' is head elt in 'ps', and 'qs' the rest
(h,t) = span (< p*p) xs -- 'h' are elts below p^2 in 'xs'
-- and 't' are the rest
и для сита Эратосфена , , разработанного Ричардом Бердом , как видно из той статьи JFP, упомянутой в другом ответе здесь,
primes = 2 : minus [3..]
(foldr (\p r-> p*p : union [p*p+p, p*p+2*p..] r) [] primes)
-- function of 'p' and 'r', that returns
-- a list with p^2 as its head elt, ...
Короткий и быстрый. (minus a b
- это список a
со всеми удаляемыми из него эльтами b
; union a b
- это список a
со всеми эльтами из b
постепенно добавляется к нему без дубликатов; оба имеют дело с упорядоченными, неубывающими списками ). foldr
- это правый сгиб списка. Поскольку он является линейным, он работает на ~ n^1.33
, для того чтобы он работал на ~ n^1.2
, можно использовать древовидное сворачивание * функция foldi
).
Ответ на ваш второй вопрос также: да . Ваш второй код, переписанный в том же «псевдокоде»,
ps = 2 : [i | i <- [3..], all ((> 0).rem i) (takeWhile ((<= i).(^2)) ps)]
очень похоже на описанное выше оптимальное сито TD - для каждого кандидата проверяется все простые числа ниже его квадратного корня. В то время как сито организует это с последовательностью отложенных фильтров во время выполнения, последнее определение заново выбирает необходимые простые числа для каждого кандидата. Один может быть быстрее другого, в зависимости от компилятора, но оба по сути одинаковы.
И третий тоже да : сито из Эратосфена лучше,
ps = 2 : 3 : minus [5,7..] (unionAll [[p*p, p*p+2*p..] | p <- drop 1 ps])
unionAll = foldi union' [] -- one possible implementation
union' (x:xs) ys = x : union xs ys
-- unconditionally produce first elt of the 1st arg
-- to avoid run-away access to infinite lists
Похоже, что это может быть реализовано и в Scala, судя по сходству других фрагментов кода.(Хотя я не знаю Скала).unionAll
здесь реализуется древовидная структура сгиба (щелкните для изображения и полного кода) , но также может быть реализована с помощью скользящего массива, работающего сегмент за сегментом вдоль потоков кратных простых чисел.
TL; DR: да, да и да.