Я делаю проект на данный момент, и мне нужен эффективный метод вычисления простых чисел. Я использовал решето Eratosthenes, но я искал вокруг и обнаружил, что сит Аткина - более эффективный метод. Мне было трудно найти объяснение (которое я смог понять!) Этого метода. Как это работает? Пример кода (желательно на C или python) был бы блестящим.
Изменить: спасибо за вашу помощь, единственное, что я до сих пор не понимаю, - это то, что переменные x и y ссылаются на псевдокод. Может ли кто-то пролить свет на это для меня?
Ответ 2
Статья в Википедии содержит объяснение:
- "Алгоритм полностью игнорирует любые числа, делящиеся на два, три или пять. Все числа с четным остатком по модулю шесть делятся на два и не просто. Все числа с остатком по модулю шестьдесят, делящимися на три, также делятся на три и не простые. Все числа с остатком по модулю шестьдесят, делящимися на пять, делятся на пять и не просто. Все эти остатки игнорируются."
Начнем с знаменитого
primes = sieve [2..] = sieve (2:[3..])
-- primes are sieve of list of 2,3,4... , i.e. 2 prepended to 3,4,5...
sieve (x:xs) = x : sieve [y | y <- xs, rem y x /= 0] -- set notation
-- sieve of list of (x prepended to xs) is x prepended to the sieve of
-- list of `y`s where y is drawn from xs and y % x /= 0
Посмотрите, как это происходит для нескольких первых шагов:
primes = sieve [2..] = sieve (2:[3..])
= 2 : sieve p2 -- list starting w/ 2, the rest is (sieve p2)
p2 = [y | y <- [3..], rem y 2 /= 0] -- for y from 3 step 1: if y%2 /= 0: yield y
p2
должен содержать не кратность 2. IOW, он будет содержать только числа, совпадающие с 2 — число в p2
не имеет числа 2. Чтобы найти p2
, нам действительно не нужно проверять деление на 2 числа в [3..]
(это 3 и вверх, 3,4,5,6,7,...), так как мы можем перечислить все кратные из 2 заранее:
rem y 2 /= 0 === not (ordElem y [2,4..]) -- "y is not one of 2,4,6,8,10,..."
ordElem
похож на elem
(т.е. is-element test), он просто предполагает, что его аргумент списка является упорядоченным, увеличивая список номеров, чтобы он мог безопасно обнаруживать и не присутствовать:
ordElem y xs = take 1 (dropWhile (< y) xs) == [y] -- = elem y (takeWhile (<= y) xs)
Обычный elem
не предполагает ничего, поэтому должен проверять каждый элемент его аргумента списка, поэтому он не может обрабатывать бесконечные списки. ordElem
может. Итак, тогда
p2 = [y | y <- [3..], not (ordElem y [2,4..])] -- abstract this as a function `diff`:
= diff [3..] [2,4..] -- diff ys xs = [y | y <- ys, not (ordElem y xs)]
-- 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23
-- . 4 . 6 . 8 . 10 . 12 . 14 . 16 . 18 . 20 . 22 .
= diff [3..] (map (2*) [2..]) -- y > 2, so [4,6..] is enough
= diff [2*k+x | k <- [0..], x <- [3,4]] -- "for k from 0 step 1: for x in [3,4]:
[2*k+x | k <- [0..], x <- [ 4]] -- yield (2*k+x)"
= [ 2*k+x | k <- [0..], x <- [3 ]] -- 2 = 1*2 = 2*1
= [3,5..] -- that 3,5,7,9,11,...
p2
- это всего лишь список всех коэффициентов выше 2. Ну, мы это знали. Как насчет следующего шага?
sieve p2 = sieve [3,5..] = sieve (3:[5,7..])
= 3 : sieve p3
p3 = [y | y <- [5,7..], rem y 3 /= 0]
= [y | y <- [5,7..], not (ordElem y [3,6..])] -- 3,6,9,12,...
= diff [5,7..] [6,9..] -- but, we've already removed the multiples of 2, (!)
-- 5 . 7 . 9 . 11 . 13 . 15 . 17 . 19 . 21 . 23 . 25 . 27 .
-- . 6 . . 9 . . 12 . . 15 . . 18 . . 21 . . 24 . . 27 .
= diff [5,7..] (map (3*) [3,5..]) -- so, [9,15..] is enough
= diff [2*k+x | k <- [0..], x <- [5]] (map (3*)
[2*k+x | k <- [0..], x <- [3]] )
= diff [6*k+x | k <- [0..], x <- [5,7,9]] -- 6 = 2*3 = 3*2
[6*k+x | k <- [0..], x <- [ 9]]
= [ 6*k+x | k <- [0..], x <- [5,7 ]] -- 5,7,11,13,17,19,...
И следующий,
sieve p3 = sieve (5 : [6*k+x | k <- [0..], x <- [7,11]])
= 5 : sieve p5
p5 = [y | y <- [6*k+x | k <- [0..], x <- [7,11]], rem y 5 /= 0]
= diff [ 6*k+x | k <- [0..], x <- [7,11]] (map (5*)
[ 6*k+x | k <- [0..], x <- [5, 7]] ) -- no mults of 2 or 3!
= diff [30*k+x | k <- [0..], x <- [7,11,13,17,19,23,25,29,31,35]] -- 30 = 6*5 = 5*6
[30*k+x | k <- [0..], x <- [ 25, 35]]
= [ 30*k+x | k <- [0..], x <- [7,11,13,17,19,23, 29,31 ]]
Это последовательность, с которой работает сито Аткина. В нем нет кратных чисел 2, 3 или 5. Аткин и Бернштейн работают по модулю 60, т.е. Удваивают диапазон:
p60 = [ 60*k+x | k <- [0..], x <- [1, 7,11,13,17,19,23,29,31, 37,41,43,47,49,53,59]]
Затем они используют некоторые сложные теоремы, чтобы знать некоторые свойства каждого из этих чисел и иметь дело с каждым соответствующим образом. Все это повторяется (a-la "колесо" ) с периодом 60.
- "Все числа n с по модулем шестидесятым остатком 1, 13, 17, 29, 37, 41, 49 или 53 (...) являются первичными тогда и только тогда, когда число решений
4x^2 + y^2 = n
является нечетным, а число является квадратным."
Что это значит? Если мы знаем, что число решений этого уравнения нечетно для такого числа, то оно просто, если оно не имеет квадратов. Это означает, что у него нет повторяющихся факторов (например, 49, 121 и т.д.).
Аткин и Бернстайн используют это, чтобы уменьшить количество операций в целом: для каждого простого (от 7 и выше) мы перечисляем (и отмечаем для удаления) кратность его квадрата, поэтому они намного дальше друг от друга, чем в сите из Эратосфена, поэтому их меньше в заданном диапазоне.
Остальные правила:
-
"Все числа n с остатком по модулю шесть, 7, 19, 31 или 43 (...) являются первичными тогда и только тогда, когда число решений для 3x^2 + y^2 = n
нечетно, а число без квадратов."
-
"Все числа n с остатком по модулю шесть, 11, 23, 47 или 59 (...) являются первичными тогда и только тогда, когда число решений для 3x^2 − y^2 = n
нечетно, а число без квадратов."
Это относится ко всем 16 номерам ядра в определении p60
.
см. также: Какой самый быстрый алгоритм для поиска простых чисел?
Временная сложность сита эратосфена при получении простых до N равна O (N log log N), а время решетки Аткина - O (N) (откладывание дополнительных оптимизаций log log N
t хорошо). Однако принятая мудрость заключается в том, что постоянные факторы в сите Аткина намного выше, и поэтому он может окупиться высоко выше 32-битных чисел (N > 2 32), если вообще.