Сито Аткина

Я пытаюсь выучить алгоритмы для генерации простых чисел, и я встретил Сито Аткина в Википедии. Я понимаю почти все части алгоритма, за исключением нескольких. Вот вопросы:

  • Как образуются три квадратичных уравнения? 4x ^ 2 + y ^ 2, 3x ^ 2 + y ^ 2 и 3x ^ 2-y2
  • Алгоритм в wikipedia говорит о модуле шестьдесят, но я не понимаю, как/где это используется в psudocode ниже.
  • Как отображаются эти напоминания 1,5,7 и 11?

Ниже приведен псевдокод из Википедии:

// arbitrary search limit                                                   
limit ← 1000000                                                             

// initialize the sieve                                                     
for i in [5, limit]: is_prime(i) ← false                                    

// put in candidate primes:                                                 
// integers which have an odd number of                                     
// representations by certain quadratic forms                               
for (x, y) in [1, √limit] × [1, √limit]:                                    
    n ← 4x²+y²                                                              
    if (n ≤ limit) and (n mod 12 = 1 or n mod 12 = 5):                      
        is_prime(n) ← ¬is_prime(n)                                          
    n ← 3x²+y²                                                              
    if (n ≤ limit) and (n mod 12 = 7):                                      
        is_prime(n) ← ¬is_prime(n)                                          
    n ← 3x²-y²                                                              
    if (x > y) and (n ≤ limit) and (n mod 12 = 11):                         
        is_prime(n) ← ¬is_prime(n)                                          

// eliminate composites by sieving                                          
for n in [5, √limit]:                                                       
    if is_prime(n):                                                         
        // n is prime, omit multiples of its square; this is                
        // sufficient because composites which managed to get               
        // on the list cannot be square-free                                
        is_prime(k) ← false, k ∈ {n², 2n², 3n², ..., limit}                 

print 2, 3                                                                  
for n in [5, limit]:                                                        
    if is_prime(n): print n  

Ответ 1

Псевдокод сита Аткина из цитированной вами статьи Википедии содержит ответы на ваши вопросы или обсуждение статьи, для которой Уилл Несс предоставил ссылку, хотя вы не сможете собрать эту информацию вместе. Краткие ответы заключаются в следующем:

  1. Три уравнения исходят из математического доказательства Аткина, что все простые числа будут встречаться как решения одного или нескольких из этих трех уравнений с соответствующими по модулю условиями для всех допустимых значений переменных x 'и' y ', и на самом деле он далее доказал что истинными простыми числами будут те числа, у которых есть нечетное число решений этих трех уравнений (слева как Истина при переключении нечетное число раз из начальных условий False) с модульными условиями для каждого, исключая те числа, которые делятся на квадраты найдены простые числа, меньшие или равные квадратному корню из тестируемого числа.

  2. Истинное сито Аткина основано на наборе по модулю 60 условий; этот псевдо-код представляет собой упрощение, в котором для каждого уравнения имеется меньше диапазонов условий, используя набор по модулю 12 условий (5 × 12 = 60), однако это приводит к тому, что 20% дополнительной работы выполняется из-за введенной избыточности в этом новый набор условий. Это также является причиной того, что этот упрощенный псевдокод должен начинать свое основное сканирование в 5, а не в обычном режиме 7, и делать бесплатные квадраты свободных квадратов без начальных начальных чисел с начальной стоимостью 5 при добавленной стоимости во время выполнения в качестве факторов 5 иначе не обрабатываются должным образом. Причина этого упрощения, возможно, заключается в том, чтобы пожертвовать некоторыми дополнительными накладными расходами кода, чтобы облегчить сложность кода при проверке значений, хотя это можно сделать с помощью одного табличного поиска, тогда как дополнительные более 30% в работе из-за использования modulo 12 не может быть уменьшена.

  3. "Напоминания" (должны быть записаны в виде остатков) найдены операторами "мод" в псевдокоде, который вы цитировали, который обозначает оператор modulo на любом компьютерном языке, который может использоваться, часто представленном символом "%" на компьютерных языках, таких как как C, Java, С#, F # и т.д. Этот оператор дает целочисленный остаток после целочисленного деления дивиденда (первый из чисел, перед "mod") на делитель (второй из чисел после символа "mod"). Причина, по которой остатки составляют всего четыре, а не 16, которые используются в полном модульном алгоритме 60, объясняется упрощением сокращения до алгоритма по модулю 12. Вы заметите, что с условиями по модулю 12 условие "4x" проходит 25, которое обычно будет устранено условиями по модулю 60, поэтому многие композиты, содержащие 25 в качестве фактора, должны быть устранены дополнительным простым 5-ти квадратным контролем, Аналогично, 55 передает "3x+" чек и 35 передает "3x-", проверяя, где они не будут для полного алгоритма по модулю 60.

Как обсуждалось в разделе "Обсуждение" в Википедии и намеченном выше, этот котируемый псевдокод никогда не намного быстрее, чем даже базовые варианты реалистичных реализаций Sieve of Eratosthenes (SoE), не говоря уже о той, которая использует ту же степень факторизации колес из-за ее неэффективности: переменные "x" и "y" не должны располагаться в пределах полного диапазона до квадратного корня из диапазона, просеянного для многих случаев (упомянутых в статье), правильное использование колеса modulo 60 восстанавливает избыточность в использование упрощения modulo 12 (как я упоминал выше), и в решениях квадратичных уравнений имеются модульные решетчатые структуры, так что условия с использованием (вычислительно медленных) модульных операций не должны проверяться с использованием алгоритма, который автоматически пропускает те решения, которые не удовлетворяли бы этим по модулю условиям в соответствии с решетчатыми узорами (упоминалось очень неясно в полной статье Аткина и Бернштейна).

Чтобы ответить на вопрос, который вы не спросили, но должен был: "Зачем использовать Сито Аткина?" ,

Основная причина, по которой используется сито Аткина (SoA), а не сито Эратосфена (SoE), заключается в том, что "знание Интернета" SOA работает быстрее. Существует две причины этого убеждения:

  1. Предполагается, что SoA быстрее, потому что асимптотическая вычислительная сложность для него меньше, чем для SoE в коэффициенте log (log n), где n - диапазон пронумерованных чисел. Практически говоря, исходя из диапазона от двух до мощности 32 (четыре миллиарда плюс) до двух к мощности 64 (около 2, за которыми следуют 19 нулей), этот коэффициент составляет шесть из пяти, равный 1,2. Это означает, что истинное значение SoE должно быть в 1,2 раза длиннее ожидаемого линейным отношением при просеивании до 64-битного диапазона чисел по сравнению с 32-битным диапазоном чисел, тогда как SoA будет иметь линейную зависимость, если бы все были идеальными, Вы можете оценить это, во-первых, это очень малый фактор для такой огромной разницы в числовых диапазонах, и, во-вторых, это преимущество справедливо только в том случае, если два алгоритма имеют одинаковую или близкую к одной и той же производительности в некотором разумном диапазоне простых чисел.

    То, что не ясно понято в этом "знании Интернета", состоит в том, что эти цифры применяются только тогда, когда сравнивается отношение производительности по заданному диапазону по сравнению с другим заданным диапазоном для одного и того же алгоритма, а не как сравнение между различными алгоритмами. Таким образом, это бесполезно, поскольку доказательство того, что SoA будет быстрее, чем SoE, поскольку SoA может начать с больших накладных расходов для заданного диапазона сит конкретного алгоритма SoE, как в следующем оптимизированном примере SoE.

  2. Считается, что SoA быстрее из-за вычислительного сравнения, сделанного и опубликованного Аткиным и Бернштейном в соответствии с их статьей, связанной в статье в Википедии. Хотя работа является точной, она применима только к искусственным ограничениям, которые они применяют в алгоритме SoE, который они сравнивали: поскольку алгоритм SoA основан на факторизации по модулю 60, который представляет собой два 2,3,5 вращения факторизационных колес, они также ограничивают SoE алгоритм к той же факторизации колес. Выполняя это, SoE выполняет около 424 000 сложных операций с отбираемыми числами за один миллиард испытанного диапазона, тогда как полностью оптимизированный SoA, как было протестировано, имеет около 326 000 комбинированных операций переключения и квадратного свободного отбраковки, каждый из которых занимает примерно одно и то же время, в SoE из-за написания в том же стиле. Это означает, что SoA примерно на 30% быстрее, чем SoE для этого конкретного набора условий факсимильной обработки колес, и это точно о том, что показали сравнительный тест Atkins и Bernstein.

    Однако SoA не реагирует на дальнейшие уровни факторизации колес, поскольку уровень 2,3,5 "запекается" в алгоритме, тогда как SoE реагирует на дальнейшие уровни: используя колесо 2,3,5,7 факторизация приводит к примерно тому же количеству операций, что означает примерно такую же производительность для каждого. Можно использовать даже частичный более высокий уровень факторизации колес, чем уровень 2,3,5,7, чтобы получить количество операций для SoE на 16,7% меньше, чем SoA, для пропорциональной лучшей производительности. Оптимизация, требуемая для реализации этих дополнительных уровней факторизации колес, на самом деле проще, чем сложность кода для реализации исходного оптимизированного SoA. Объем памяти для реализации этих сопоставимых реализаций с разбивкой по страницам примерно такой же, как размер буферов страниц плюс массив базовых простых чисел.

    Кроме того, обе они выиграли бы от написания в стиле "поиск состояния машины", который помог бы улучшить оптимизацию с использованием встроенного кода и разворачивания экстремальных циклов, но SoE больше походит на этот стиль, чем на SoA, из-за более простого алгоритма.

Таким образом, для сит диапазонов примерно до 32-битного числа, максимально оптимизированный SoE примерно на 20% быстрее (или больше с дальнейшей факторизации колес), чем SoA; однако SoA обладает этим преимуществом асимптотической вычислительной сложности, поэтому будет некоторый момент, когда он догоняет. Эта точка будет примерно в диапазоне, где отношение log (log n) к log (log (2 ^ 32)) или 5 равно 1,2 или диапазон примерно в 2 раза десять до девятнадцатой мощности - чрезвычайно большое число. Если оптимизированный SoA-запуск на современном настольном компьютере должен занимать около трети секунды для вычисления простых чисел в 32-битном диапазоне чисел, и если бы реализация была идеальной, так как при увеличении линейного увеличения с увеличением диапазона это потребовало бы около 45 лет, чтобы вычислить простые числа в этом диапазоне. Однако анализ простых чисел в этих высоких диапазонах часто делается небольшими кусками, для которых использование SoA было бы теоретически практичным по сравнению с SoE для очень больших сит, но с очень небольшим коэффициентом.

EDIT_ADD: На самом деле ни сегментированная страница SoE, ни SoA не работают в линейном времени для этих огромных диапазонов по сравнению с более низкими диапазонами, так как оба сталкиваются с проблемами, когда операции "переключения" и "отбраковки" теряют эффективность из-за того, что пропускать большое количество страниц за каждый прыжок; это означает, что для того, чтобы справиться с этим "перескакиванием страницы", для обоих потребуется модифицированные алгоритмы, и очень небольшое преимущество для SoA может быть полностью стерто, если есть какие-либо незначительные различия в том, как это делается. SoA имеет гораздо больше терминов в "таблицах перехода", чем SoE, примерно в обратном соотношении между количеством простых чисел, найденным до квадратного корня диапазона от этого диапазона, и это, скорее всего, добавит O (log n) как для обработки, так и для постоянного увеличения коэффициента увеличения для SoA, который имеет большее количество записей в "таблице перехода". Этот дополнительный факт в значительной степени полностью компенсирует любое преимущество SoA над SoE даже для чрезвычайно больших диапазонов. Кроме того, SoA имеет постоянные накладные расходы для более индивидуальных циклов, требуемых для многих других случаев при реализации условий для трех отдельных квадратичных уравнений плюс цикл "простых квадратов", а не только простой цикл отбора, поэтому никогда не может иметь среднее вычислительное время на операцию как SoE при полной оптимизации. END_EDIT_ADD

EDIT_ADD2: Я считаю, что большая часть путаницы в отношении Сита Аткина объясняется недостатками псевдокода из статьи в Википедии, как цитируется в вопросе, так что придумали несколько лучшую версию псевдокода, который обращается к по крайней мере, некоторые недостатки, связанные с диапазоном переменных "x" и "y", и путаница по модулю 12 по сравнению с модулем 60 следующим образом:

limit ← 1000000000        // arbitrary search limit

// Initialize the sieve
for i in {7,11,13,17,19,23,29,31, 37,41,43,47,49,53,59,61,...}:
    is_prime(i) ← false

// Put in candidate primes:
// integers which have an odd number of
// representations by certain quadratic forms.
while n ≤ limit, n ← 4x²+y² where x ∈ {1,2,...} and y ∈ {1,3,...} // odd y's
    if n mod 60 ∈ {1,13,17,29,37,41,49,53}:
        is_prime(n) ← ¬is_prime(n)
while n ≤ limit, n ← 3x²+y² where x ∈ {1,3,...} and y ∈ {2,4,...} // only odd 
    if n mod 60 ∈ {7,19,31,43}:                            // x and even y's
        is_prime(n) ← ¬is_prime(n)
while n ≤ limit, n ← 3x²-y² where x ∈ {2,3,...} and y ∈ {x-1,x-3,...,1} //all 
    if n mod 60 ∈ {11,23,47,59}:                   // even/odd odd/even combos
        is_prime(n) ← ¬is_prime(n)

// Eliminate composites by sieving, only for those occurrences on the wheel
for n² ≤ limit where n ∈ {7,11,13,17,19,23,29,31, 37,41,43,47,49,53,59,61,...}:
    if is_prime(n):
        // n is prime, omit multiples of its square; this is
        // sufficient because composites which managed to get
        // on the list cannot be square-free
        while c ≤ limit, c ← n² × k where
                      k ∈ {1,7,11,13,17,19,23,29, 31,37,41,43,47,49,53,59,...}:
            is_prime(c) ← false

output 2, 3, 5
for n ≤ limit when n ← 60 × k + x where
  k ∈ {0..} and
  x ∈ {7,11,13,17,19,23,29,31, 37,41,43,47,49,53,59,61}:
    if is_prime(n): output n

Вышеизложенное кажется довольно простым и является довольно хорошим алгоритмом, за исключением того, что он все еще не быстрее, чем базовое сито Эратосфена, которое использует одно и то же колесо факторизации 2; 3; 5, потому что оно тратит почти половину своих внутренних операций переключения циклов, которые не срабатывают модульные тесты. Показывать:

Ниже приведен повторяющийся образец 4x² +y ² для квалификации по модулю 60 в диапазоне 15 значений "x" (каждое значение) по вертикали и 15 нечетных значений "y" по горизонтали; оба начинаются с одного. Обратите внимание на то, что они симметричны относительно вертикальной оси, но только 128 из 225 или 256 из 450 для полного диапазона 30 значений "x" являются действительными операциями переключения:

  0 13 29 53  0  0 53 49 53  0  0 53 29 13  0
 17  0 41  0 37 17  0  1  0 17 37  0 41  0 17
 37  0  1  0  0 37  0  0  0 37  0  0  1  0 37
  0 13 29 53  0  0 53 49 53  0  0 53 29 13  0
 41 49  0 29  1 41 29  0 29 41  1 29  0 49 41
  0  0 49 13  0  0 13  0 13  0  0 13 49  0  0
 17  0 41  0 37 17  0  1  0 17 37  0 41  0 17
 17  0 41  0 37 17  0  1  0 17 37  0 41  0 17
  0  0 49 13  0  0 13  0 13  0  0 13 49  0  0
 41 49  0 29  1 41 29  0 29 41  1 29  0 49 41
  0 13 29 53  0  0 53 49 53  0  0 53 29 13  0
 37  0  1  0  0 37  0  0  0 37  0  0  1  0 37
 17  0 41  0 37 17  0  1  0 17 37  0 41  0 17
  0 13 29 53  0  0 53 49 53  0  0 53 29 13  0
  1  0  0 49  0  1 49  0 49  1  0 49  0  0  1

Ниже приведен повторяющийся шаблон 3x² +y ² для определения по модулю 60 в диапазоне 5 нечетных значений "x" по вертикали и 15 четных значений "y" по горизонтали. Обратите внимание, что они симметричны относительно горизонтальной оси, но только 48 из 75 или 144 из 450 для полного диапазона 30 значений "x" являются действительными для переключения операций, поскольку все 144 из 450 недействительных операций с четными "x" и нечетными "y" уже устранены:

  7 19  0  7 43  0 19 19  0 43  7  0 19  7  0
 31 43  0 31  7  0 43 43  0  7 31  0 43 31  0
 19 31  0 19  0  0 31 31  0  0 19  0 31 19  0
 31 43  0 31  7  0 43 43  0  7 31  0 43 31  0
  7 19  0  7 43  0 19 19  0 43  7  0 19  7  0

Ниже следует повторяющийся шаблон 3x² -y ², определяющий по модулю 60, в диапазоне от 5 нечетных значений "x" по вертикали и 15 четных значений "y" по горизонтали. Обратите внимание, что они симметричны относительно горизонтальной оси, но только 48 из 75 или 224 из 450 для полного диапазона 30 значений "x" являются действительными операциями переключения:

 59 47  0 59 23  0 47 47  0 23 59  0 47 59  0
 23 11  0 23 47  0 11 11  0 47 23  0 11 23  0
 11 59  0 11  0  0 59 59  0  0 11  0 59 11  0
 23 11  0 23 47  0 11 11  0 47 23  0 11 23  0
 59 47  0 59 23  0 47 47  0 23 59  0 47 59  0

Ниже приведен повторяющийся шаблон 3x² -y ², определяющий по модулю 60, в диапазоне 5 четных значений "x" по вертикали и 15 нечетных значений "y" по горизонтали. Обратите внимание, что они симметричны относительно вертикальной оси, но только 48 из 75 или 224 из 450 для полного диапазона 30 значений "x" являются действительными операциями переключения:

 11  0 47 23  0 11 23  0 23 11  0 23 47  0 11
 47  0 23 59  0 47 59  0 59 47  0 59 23  0 47
 47  0 23 59  0 47 59  0 59 47  0 59 23  0 47
 11  0 47 23  0 11 23  0 23 11  0 23 47  0 11
 59  0  0 11  0 59 11  0 11 59  0 11  0  0 59

Как можно определить путем проверки приведенных выше таблиц, для псевдокода, как указано выше, существует общий диапазон повторения 30 значений x (включая как коэффициенты, так и коэффициенты), которые имеют только 688 действительных операций из общего количества 1125 комбинированных операций; таким образом, он тратит почти половину своей обработки только при условном пропускании значений из-за того, что непроизводительные операции пропусков являются частью самых внутренних циклов. Существует два возможных способа устранения этой "удачной" избыточности неэффективно:

  1. Метод, описанный в статье Аткина и Бернштейна, который использует распознанные шаблоны в комбинированных модулях "х" и "у" для обработки только "по модулю" "хитов", используя тот факт, что как только один находит данный модуль по шаблону, то существует бесконечная последовательность значений при некотором модуле (равном положению битов колеса), где каждый шаблон разделяется легко вычисленным (переменным) смещением, которое имеет форму "текущая позиция плюс А, умноженная на текущее значение" x "плюс B "и" текущая позиция плюс C раз текущее значение 'y' плюс D ", где A, B, C и D, являются простыми константами (простое значение легко обрабатывается легко). Бернштейн использовал этот метод с дополнительной помощью многих таблиц Look Up (LUT).

  2. Метод создания общего состояния шаблона Look Up Tables (LUT) (по одному для каждой из четырех таблиц выше плюс один для младшего свободного квадрата свободного квадрата), индексированных по модулю текущими значениями 'x' в сочетании с модулем 'y' чтобы найти параметры A, B, C и D, чтобы пропустить, а не следующий шаблон, а только в следующую позицию в горизонтальной последовательности. Вероятнее всего, это более высокопроизводительный вариант, поскольку он более легко позволяет использовать экстремальный тактовый цикл для каждой операции с использованием встроенного кода разворачиваемого цикла и будет генерировать более эффективный код для больших диапазонов, когда сегментирование страниц по мере того, как скачки за цикл в среднем меньше, Это, скорее всего, приведет к тому, что тактовые циклы на петлю будут близки к высокооптимизированному сите Эратосфена, как в простых случаях, но вряд ли он достигнет такого низкого уровня из-за необходимости вычислять размеры шага переменной, а не использовать фиксированные первичные смещения для SoE.

Таким образом, есть три руководящие задачи в сокращении времени работы основного сита:

  1. Успешное сито уменьшает количество операций, которые даже "оптимизированный удар" SoA не удается по сравнению с высокооборотным факторизованным SoE примерно на 16,7% для диапазонов в несколько миллиардов.

  2. Успешное сито уменьшает циклы тактовых импульсов процессора на операцию, которые SoA терпит неудачу по сравнению с высокооптимизированным SoE, таким как primesieve, потому что его операции более сложны из-за переменных приращений, опять же, вероятно, на 20-60%. Atkin и Bernstein primegen (SoA) занимают около 4,5 по сравнению с примерно 2,5 тактами за операцию для primesieve (SoE) для диапазона в один миллиард для каждого, но, возможно, SoA можно было бы ускорить, заимствуя некоторые из методов оптимизации из primesieve.

  3. Успешное сито имеет достаточно низкую сложность кода, так что его можно более легко расширить до больших диапазонов, используя такие методы, как "просеивание ковша" и другие оптимизации сегментации страниц. Для этого сито Аткина терпит неудачу, поскольку оно становится экспоненциально более сложным для сегментирования страниц с большим числом диапазонов. Очень сложно написать программу SoA, которая будет конкурировать с даже Bernstein primegen (SoA), не говоря уже о primesieve, тогда как довольно легко написать код, который близок к той же производительности, что и primesieve.

  4. Успешное сито имеет более низкую эмпирическую сложность, которую SoA имеет выше SoE в зависимости от (log (log n), где n - верхний диапазон, который нужно просеять, но это дополнительное небольшое соотношение вряд ли будет когда-либо достаточным, чтобы составить для вышеуказанных двух верхних коэффициентов потерь, поскольку этот коэффициент становится все меньше с увеличением диапазона. END_EDIT_ADD2

Итак, ответ на вопрос "Зачем использовать Сито Аткина?" "Нет причин использовать его вообще, если SoE реализуется с максимальными оптимизациями факторизации колес до тех пор, пока просеиваемые числа не будут чрезвычайно большими (64-битный диапазон номеров и выше), а затем преимущество SoA очень мало и, возможно, невозможно реализовать при все в зависимости от очень незначительных настроек в относительной оптимизации ". ,

В качестве ответа на другое подобное сообщение "Сито Аткина" я опубликовал страницу с сегментированной версией С# для SoE, оптимизированную согласно приведенному выше: fooobar.com/questions/38295/....

Ответ 2

В моем оригинальном ответе на этот вопрос, чтобы помочь лучше понять Сито Алотима Аткина, я продлил синтаксис Википедии Аткина (SoA) для исправления некоторых недостатков в упрощенном алгоритме "modulo 12", который многие считают запутанным по сравнению с полным алгоритмом "по модулю 60". Кроме того, оригинальный алгоритм псевдокода Wikipedia приводит к неэффективным реализациям; в частности, диапазоны переменных "x" и "y" кажутся шире, чем необходимо, и упрощение по модулю 12 приводит к дополнительному 20% избыточному результату. Однако, как обсуждалось в этом ответе, этот псевдокод по-прежнему неэффективен, поскольку модульные тесты все еще находятся в самых внутренних циклах квадратичного решения, и поэтому почти половина таких циклов решений непродуктивна для тех решений, которые не проходят модульные тесты; это означает, что программа, реализующая этот псевдо-код, скорее всего, будет почти в два раза медленнее, если это необходимо, даже если нет узкого места в скорости доступа к памяти.

Алгоритм, используемый Аткиным и Бернштейном в их программе "primegen" , чтобы избежать избыточности этого цикла, может быть лучше понят в результате следующих наблюдений, основанных на таблицах "попадания" в моем предыдущем ответе:

  • Симметрия таблиц "попадание" зависит от того, является ли "y" нечетным (вертикальная симметрия) или даже (горизонтальная симметрия).

  • Таким образом, если бы мы хотели, чтобы ход шаблонов продолжался в горизонтальном направлении, как показано на рисунке, мы можем перевернуть порядок петель 'x' и 'y' для "3x +" и "3x- нечетные x → четные y" случаи, означающие нечетные случаи 'x'.

  • Теперь легко устранить нулевые случаи для двух перевернутых таблиц "3x", которые имеют только пять повторений по горизонтали по условию во внешнем цикле, как те случаи, когда "y mod 3 равно 0" плюс три дополнительных случаев только для столбца средней оси, где "y mod 5 равно 0".

  • Для последнего случая "3x-, even x → odd y" легко удалить большую часть нуля, просто отфильтровывая те случаи, когда "(y + 2) mod 3 равно 0" плюс дополнительные два случая только для последней строки, где "(x mod 60) равно 59" где "(y + 2) mod 5 равно 0" (дублирующие исключения для столбца симметричной вертикальной оси, который всегда исключается). Хотя эти "y-тесты", по-видимому, должны произойти во внутреннем цикле, так как есть только пять "ударов" на сторону вертикальной оси симметрии, они могут быть встроены в цикл "x" .

  • Наиболее трудные исключения для таблицы "4x", где шаблон более сложный: существует пять различных горизонтальных шаблонов, где каждый может быть распознан, потому что "(x mod 60) для первого столбца" отличается, с одной из ведущих шаблонов нулевой строки в первой таблице выше, имеющей по модулю 5, а другой - по модулю 25; теперь пять различных шаблонов могут быть определены путем "вставки" случаев в виде смещений по обе стороны от вертикальной симметричной оси для каждого шаблона.

  • Расстояние до следующей строки равно 4 × ((x + 1) ² - x²) или 8x + 4 для первой таблицы, (y + 2) ² - y² или 4y + 4 для второго две (замененные) новые таблицы и 3 × ((x + 2) ² - x²) или 12x + 12 для последней таблицы.

  • Для всех (сейчас) вертикально-симметричных таблиц смещение центральной оси может быть определено из смещения первого столбца как (y + 16) ² - y² или 32y + 256 для длинных строк и 3 × ((x + 4) ² - x²) или 24x + 48 для коротких строк.

  • Аналогичным образом симметричные смещения по обе стороны от вертикальной оси могут быть легко вычислены по смещению вертикальной оси, например, по паре (y + 2) ² - y² или 4 + 4x с (y - 2) ² - y² или 4 - 4y где y - смещение центральной оси для длинного ряда и плюс и минус два положения; случай для коротких строк "x" случаев аналогичен, но просто умножается на три для общего масштаба значений "3x" x.

  • Вышеуказанные фильтры, определяемые условиями горизонтальной переменной, как представляется, нарушают нашу цель устранения условного кода во внутренних циклах, но это правило не применяется, когда определение шаблона не является внутренним циклом, а скорее начинается целая серия новые внутренние петли по всем шаблонам по горизонтали в массиве для каждого допустимого элемента шаблона. Это работает, потому что мы можем математически показать, что каждая из одинаковых эквивалентных позиций в следующем шаблоне (который имеет тот же самый модуль) разделяется следующим образом: для горизонтального шаблона вперед на 30 для длинной строки шаблоны разделяются (x + 30) ² - x² или 60x + 900, так 60 × (x + 15); для горизонтального рисунка на 10 для коротких рядов, рисунки разделяются на 3 × ((x + 10) ² - x²), поэтому 60 * (x + 5). Поскольку оба имеют по модулю 60 нуля, это точное объяснение того, почему повторяются шаблоны одного и того же модуля в каждой позиции. Таким образом, как только мы найдем смещение для каждой позиции верхнего левого угла, мы можем определить по модулю и смещению для начальных позиций для ненулевых позиций шаблона и просто свернуть внутренний цикл, используя отношения шаблонов от этой точки до предела решетки решета.

  • Все приведенные выше приращения столбца и строки могут быть выполнены без использования операции "разделить" с использованием дополнения по модулю, где алгоритм отслеживает пары частных и по модулю 60 с помощью только "проверки и настройки переполнения", работа над партой modulo/quotient после каждой операции, но условный код для "проверки и настройки", скорее всего, будет более дорогостоящим по сравнению с тем, как написано, для чего большинство современных оптимизирующих компиляторов будут генерировать многократную операцию с использованием характеристик усечения переполнения для замените вычислительно дорогостоящую операцию деления на деление на маленькие целые числа, что обычно занимает меньше времени, чем комбинация операций плюс условный код, необходимый для метода "проверить и настроить" или с помощью операции прямого деления.

  • Вышеупомянутые наборы исключений работают очень хорошо для упакованного представления бит простых чисел в том, что каждый из 16 действительных значений по модулю может быть представлен как один бит в 16-битовом слове, а индекс слова представляет индексы каждого колеса по модулю 60; таким образом, мы можем продвигаться на четные приращения, равномерно делящиеся на 60, просто продвигая целое число 16-разрядных слов.

Для использования в качестве нестраничного сегментированного алгоритма, как для псевдокода здесь, достаточно просто переместить модульные проверки из самого внутреннего цикла, не исключая всех "ударов промаха" для получающегося среднего цикла, так как хотя они по-прежнему не являются максимально эффективными в том смысле, что средний цикл все еще обрабатывает "хит-релизы", который не добавляет более одного процента к времени выполнения; однако для выгружаемых реализаций "пропущенные удары" даже во внешних циклах могут значительно увеличиться для вычислительных издержек, поскольку эти циклы выполняются один раз на странице в диапазоне значений "х" для этой страницы, поэтому существует коэффициент около логарифма квадратного корня просеянного диапазона больше внешних петель для SoA по сравнению с ситом эратосфенов (SoE). Версия более сложного псевдокода, который представляет (необязательно битовую) версию, не подверженную сегментированию страницы, "оптимизированного по удалению" метода Аткина и Бернштейна, может быть записана следующим образом:

// arbitrary search limit
limit ← 1000000000

// the set of base wheel prime candidates
s ∈ {7,11,13,17,19,23,29,31, 37,41,43,47,49,53,59,61}

// Initialize the sieve as an array of wheels with
// enough wheels to include the representation for limit
for n ← 60 * w + x where w ∈ {0,...(limit - 7) ÷ 60}, x in s:
    sieve(n) ← false // start with no primes.

// Put in candidate primes:
// integers which have an odd number of
// representations by certain quadratic forms.
while n ≤ limit when n ← 4x²+y²            // all x and only odd y's
        where x ∈ {1,...}, y ∈ {1,31,...}: // skip by pattern width in y
    for cb ≤ limit when midy ← y + i, cb ← n - y² + midy²
            where i ∈ {0,2,...28}: // middle level loop to "hit" test modulos
        cbd ← (cb - 7) ÷ 60, cm ← cb mod 60
        if cm in {1,13,17,29,37,41,49,53}:
            while c ≤ limit when c ← 60 × (cbd - midy² + ((midy + 15 × j) × j)²) + cm
                    where j  {0,...}:
                sieve(c) ← ¬sieve(c) // toggles the affected bit
while n ≤ limit when n ← 3x²+y²              // only odd x and even y's
        where x ∈ {1,3,...}, y ∈ {2,32,...}: // skip by pattern width in y
    for cb ≤ limit when midy ← y + i, cb ← n - y² + midy²
            where i ∈ {0,2,...28}: // middle level loop to "hit" test modulos
        cbd ← (cb - 7) ÷ 60, cm ← cb mod 60
        if cm in {7,19,31,43}:
            while c ≤ limit when c ← 60 × (cbd - midy² + ((midy + 15 × j) × j)²) + cm
                    where j  {0,...}:
                sieve(c) ← ¬sieve(c) // toggles the affected bit
while n ≤ limit when n ← 3x²-y²                    //even/odd & odd/even combos
        where x ∈ {2,3,...}, y ∈ {x-1,x-31,...,1}: // skip by pattern width in y
    for midy ≥ 1 and cb ≤ limit when midy ← y - i, cb ← n + y² - midy²
            where i ∈ {0,2,...28}: // middle level loop to "hit" test modulos
        cbd ← (cb - 7) ÷ 60, cm ← cb mod 60
        if cm in {11,23,47,59}:
            while yn ≥ 1 and c ≤ limit when yn = midy - 30 * j and
                             c ← 60 × (cbd + midy² - ((midy - 15 × j) × j)²) + cm
                    where j  {0,...}:
                sieve(c) ← ¬sieve(c) // toggles the affected bit

// Eliminate prime squares by sieving, only for those occurrences on the wheel
for sqr ≤ limit where sqr ← n², n in {7,11,13,17,19,23,29,31, 37,41,43,47,49,53,59,61,...}:
    if is_prime(n):
        // n is prime, omit multiples of its square; this is
        // sufficient because composites which managed to get
        // on the list cannot be square-free
        if c0 ≤ limit where c0 ← sqr × (m mod 60) where m in s:
            c0d ← (c0 - 7) ÷ 60, cm ← (c0 - 7) mod 60 + 7 // means cm in s
            while c ≤ limit for c ← 60 × (c0d + sqr × j) + cm
                    where j ∈ {0,...}:
                sieve(c) ← false       // cull composites of this prime square

output 2, 3, 5
for n ≤ limit when n ← 60 × k + x where k ∈ {0..}, x in s:
    if sieve(n): output n

В частности, обратите внимание на то, как самые внутренние квадратичные петли очень просты, но увеличиваются на линейно увеличивающуюся переменную сумму за цикл, тогда как простое исключение простых квадратов, которое использует тип прогрессирования Эратосфена, увеличивается на фиксированную сумму "sqr". Эти простые циклы будут выполняться довольно быстро на современном процессоре, занимая около 4 тактовых циклов ЦП на каждый цикл, если доступ к памяти будет достаточно быстрым, как для небольших диапазонов сита, или как при использовании сегментированной страницы, где страницы подходят в кэшах процессора L2/L1 которые имеют время доступа к памяти около этого диапазона.

Когда массив большой, как и во многих мегабайтах, среднее время доступа к памяти, вероятно, будет составлять среднее значение от 20 до 100 тактовых импульсов ЦП (в зависимости от скорости работы ОЗУ, эффективности доступа к памяти CPU и эффективность использования промежуточных кэшей), и это будет основным ограничивающим фактором в скорости выполнения, а не герметичностью циклов, кроме количества операций переключения/отбраковки, которые требует алгоритм. Эти конкретные алгоритмы, как SoA, так и SoE, накладывают определенную нагрузку на интерфейс памяти для больших буферов тем, что они пропускают через буфер, увеличиваясь по окружности всего круга, умножая множитель за раз, и повторяют для разных смещений, а не обрабатывают все "хиты" колеса в последовательности; это связано с переносом тестов "попадания" в средний цикл, что экономит "ударное" тестирование, но, как следствие, увеличивает этот "больший" буфер, со средним "прыжком" для SoA выше, чем для SoE. Таким образом, оценка времени выполнения для этого алгоритма в диапазоне чисел до миллиарда составляет около 60 тактовых циклов ЦП на операцию (что будет меньше для коэффициента SoE из-за меньшего среднего "прыжка" ), умноженного на количество операций toggle/cull (около 260 000 для сита Atkin), деленное на тактовую частоту процессора. Поэтому процессор с тактовой частотой 3,5 ГГц будет выполнять этот алгоритм в диапазоне до миллиарда за четыре секунды, и основное различие между скоростью выполнения для невыгружаемых реализаций SoA и SoE в этом стиле - это разница в эффективности доступа к памяти для больших буферы, где высокооборотная факторизация SoE более эффективна примерно в два раза.

Учитывая, что для больших буферов время доступа к памяти является предельным коэффициентом скорости, очень важно найти алгоритм с минимальным количеством операций. The SoA, реализованный Аткиным и Бернштейном в их "примерной" образце программы, приобретает постоянный фактор "хитовых" операций как отношение к диапазону просеивания и, реализуя даже простую версию программы из моего первого ответа, мы можем определить что этот коэффициент составляет около 0,26 от просеянного диапазона: это означает, что существует около 260 000 операций для диапазона сита в миллиард.

Для SoE, реализованного с высокой колесной факторизацией 210-элементного колеса с 48 первичными кандидатами "попадания" за оборот (небольшое колесо для простых чисел 2; 3; 5; 7) в сочетании с предварительным отбором дополнительным простых чисел 11, 13, 17, 19 число операций отбраковки для диапазона сита n примерно n раз (ln ((ln n)/2) + M - 1/(ln n) - 1/2 - 1/3 - 1/5 - 1/7 - 1/11 - 1/13 - 1/17 - 1/19) * 48/210, где "*" означает умноженное на, "/" означает разделение на, M - это Meissel- Mertens constant M ≈ 0.2614972128... корректируя количество операций, возникающих в результате оценки (ln ln n), обратный термин "ln n" представляет собой корректировку для отбраковки, начиная с квадрата базовых простых чисел (меньше квадратного корня диапазона n); для n одного миллиарда коэффициент по сравнению с n составляет около 0,250485568 или около 250 миллионов операций cull для диапазона до миллиарда.

Это примерно такое же количество операций, что и для SoA, и контрастирует с простым колесом 2; 3; 5, используемым в сравнительном тесте Аткина и Бернштейна, который имеет коэффициент 0,404805008 или около 400 000 операций для диапазона сит один миллиард. Это лучшее соотношение для более высокофакторизованного SoE означает, что постоянные факторы для этой вполне реализуемой реализации примерно такие же, как для SoA, за исключением любых различий в постоянных факторах из-за сложности цикла.

EDIT_ADD1: Для сравнения, следующий эквивалентный псевдокод для сильно факторизованного и предварительно отобранного SoE, написанного в том же стиле, что и вышеупомянутый алгоритм SoA:

// arbitrary search limit
limit ← 1000000000

// the set of base wheel primes
r ∈ {23,29,31,37,41,43,47,53, 59,61,67,71,73,79,83, //positions + 19
     89,97,101,103,107,109,113,121,127, 131,137,139,143,149,151,157,163,
     167,169,173,179,181,187,191,193, 197,199,209,211,221,223,227,229}

// an array of length 11 times 13 times 17 times 19 = 46189 wheels initialized
// so that it doesn't contain multiples of the large wheel primes
for n where n ← 210 × w + x where w ∈ {0,...46189}, x in r: // already
    if (n mod cp) not equal to 0 where cp ∈ {11,13,17,19}: // no 2,3,5,7
        mstr(n) ← true else mstr(n) ← false                // factors

// Initialize the sieve as an array of the smaller wheels with
// enough wheels to include the representation for limit
for n where n ← 210 × w + x, w ∈ {0,...(limit - 19) ÷ 210}, x in r:
    sieve(n) ← mstr(n mod (210 × 46189))    // init pre-culled primes.

// Eliminate composites by sieving, only for those occurrences on the
// wheel using wheel factorization version of the Sieve of Eratosthenes
for n² ≤ limit when n ← 210 × k + x where k ∈ {0..}, x in r
    if sieve(n):
        // n is prime, cull its multiples
        s ← n² - n × (x - 23)     // zero'th modulo cull start position
        while c0 ≤ limit when c0 ← s + n × m where m in r:
            c0d ← (c0 - 23) ÷ 210, cm ← (c0 - 23) mod 210 + 23 //means cm in r
            while c ≤ limit for c ← 210 × (c0d + n × j) + cm
                    where j ∈ {0,...}:
                sieve(c) ← false       // cull composites of this prime

output 2, 3, 5, 7, 11, 13, 17, 19,
for n ≤ limit when n ← 210 × k + x where k ∈ {0..}, x in r:
    if sieve(n): output n

Обратите внимание, насколько менее сложным является этот код, несмотря на то, что он более факторизован колесами, так что существует меньше постоянных накладных расходов операций, чем код SoA, тогда как самый внутренний цикл отсечения на самом деле немного проще и, следовательно, (если что-либо) вероятно, немного быстрее. Также обратите внимание, сколько должно выполняться больше внешних циклов для кода SoA, в общей сложности примерно такое же количество внешних контуров, что и квадратный корень из диапазона просеивания для SoA, где имеется только около диапазона сита, деленного на естественный логарифм квадратного корня из этих диапазонов внешних петель для SoE - фактор примерно в десять раз меньше для SoE по сравнению с SoA для диапазонов сита в один миллиард. Это компромисс, который делает SoA: более внешние контуры для меньшего количества операций на внутренний цикл с большими "прыжками" буфера между операциями; однако это также делает SoA менее эффективным для каждой операции, особенно для больших диапазонов сит. END_EDIT_ADD1

Что касается сложности кода для больших диапазонов, они всегда реализуются как сеточные сегменты страницы, чтобы избежать необходимости иметь весь массив сита в ОЗУ (ограничения памяти ограничивают диапазон) и для того, чтобы воспользоваться преимуществами ЦК кэширования, чтобы уменьшить среднее время доступа к памяти, как обсуждалось выше. Для SoE это можно легко реализовать на основе сохраненной копии представления базовых простых чисел, где примерно квадратный корень из диапазона, деленный на log этого квадратного корневого числа этого диапазона; для SoA нужно ссылаться на то же представление базовых простых чисел для полного исключения простых квадратов, но также необходимо использовать значения смещений последовательности плюс сохраненные значения "x" или "y" (или сохранить текущие значения обоих "x" 'и' y '), чтобы возобновить смещение следующей страницы с общим количеством сохраненных последовательностей, как о квадратном корне диапазона (не разделен натуральным логарифмом корня n), или, альтернативно, новый' x '/' y "Пара смещения может быть рассчитана для начала каждой новой страницы при значительных вычислительных расходах. Первый метод значительно увеличивает использование служебных данных памяти, особенно для многопоточных реализаций, где необходимо сохранить копию для каждого потока; второй не использует больше памяти, но использует больше вычислительной мощности по сравнению с SoE для этих больших диапазонов.

Сегментированные алгоритмы страницы также важны для того, чтобы в полной мере использовать современный многоядерный процессор, который может сократить время, требуемое для заданной задачи, по отношению к числу используемых сердечников, особенно для алгоритмов с разбивкой по страницам, где каждая страница обработка может быть назначена отдельному потоку.

Способ реализации "primimesieve" SoE управляет средним временем работы около 2,5 тактовых циклов ЦП по сравнению с примерно 4,5 тактовыми циклами ЦП для "пробной реализации" Аткина и Бернштейна "пример" реализации SoA с помощью экстремальной подстройки циклическая развертка с сегментацией страницы; этот метод также может быть применен к SoA, но этот конкретный псевдокод не подходит для этого, и необходимо использовать альтернативный метод оптимизации "ударов", который обрабатывает все модули в одном цикле (в квадратичном цикле уравнения); однако это сложный алгоритм, который, казалось бы, выходит за рамки этого вопроса. Достаточно сказать, что скачки в сложности до сих пор ничто по сравнению с тем, что требуется для добавления этой дополнительной оптимизации в SoA, и даже с этим соотношение внешних петель все еще делает SoE намного более эффективным.

Таким образом, хотя довольно просто реализовать наивный алгоритм SoA, как в оригинальном псевдокоде Википедии, или даже использовать более сложный псевдо-код SoA, как в этих двух ответах, чрезвычайно сложно написать то, что будет конкурировать с четными Bernstein "primegen" (SoA), не говоря уже о более быстром "primesieve" (SoE), тогда как довольно легко написать код, который близок к той же производительности, что и "primegen" , и немного больше работы, чтобы написать что-то, что конкурирует с "primesieve" с использованием алгоритма SoE с небольшими изменениями в сегментировании страницы поддержки.

EDIT_ADD2: Чтобы применить эту теорию в практическом плане, я обычно пишу примерную программу для демонстрации этих принципов; однако в этом случае у нас есть все, что нам действительно нужно в исходный код "primegen" , который является эталонной реализацией изобретателями SoA. На моей машине (i7-2600K, 3.5 ГГц) с размером буфера, скорректированным до 32768 с 8192, чтобы отразить мой размер кеша L1, эта "простая частота" просеивает простые числа до одного миллиарда примерно за 0,365 секунды. Фактически, Аткин и Бернштейн немного обманывали их сравнение с их алгоритмом решета Эратосфена, в котором им было присвоено только четыре буфера Kilobyte; корректируя этот размер буфера примерно до того же размера ( "#define B32 1001" на "#define B32 8008" ) приводит к времени выполнения около 0,42 секунды для "eratspeed" примерно до одного миллиарда, оставив его еще немного медленнее. Однако, как обсуждалось выше, он делает больше работы в том, что он выполняет около 0,4048 миллиарда операций cull, тогда как SoA составляет всего около 0,26 миллиарда комбинированных операций toggle/cull. Использование алгоритма из вышеуказанного псевдо-кода SoE для изменения этого означает, что максимальная факторизация колес означает, что SoE фактически выполнит немного меньшую работу, чем SoA, примерно на 0,2505 миллиарда, то есть время выполнения сократится примерно до двух третей или более или от 0,265 до 0,3 секунды, что делает его быстрее, чем "премьер".

Между тем, если сравнивать "primespeed" с кодом "eratspeed", вы видите, что выгружаемый SoE намного менее сложный, чем код SoA, как указано в приведенном выше псевдокоде.

Конечно, можно было бы противостоять тому, что SoA поддерживает соотношение 0,26 раза от операций диапазона просеивания до столь же высокого диапазона просеивания, как требуется, тогда как отношение SoE поднимается в соответствии с приведенными выше уравнениями - примерно на 20% для диапазон просеивания даже до ста миллиардов. Однако, учитывая небольшую настройку обоих алгоритмов, чтобы увеличить размер буфера и "подрезать" его, чтобы обработать его в L1 кеш-размерах с размерами, SoE будет выполнять, как прогнозировалось, тогда как накладные расходы SoA продолжают расти из-за дополнительного отношения среднего/внутренние петли для SoA по сравнению с SoE. В то время как у SoE увеличение операций по отбраковке примерно на 20%, у SoA есть увеличение квадратичных циклов обработки примерно на эти же 20% при очень небольшом чистом усилении, если таковые имеются. Если бы эти оптимизации были сделаны в SoA, то он мог просто догнать максимально ограниченную колесами SoE при очень большом диапазоне просеивания, например, от десяти до девятнадцатой или около того, но мы говорим о сотнях лет обработки на настольном компьютере даже с использованием многопроцессорной обработки.

SoA может быть практичным для просеивания субдиапазона (то есть одного сегмента страницы) из очень большого смещения диапазона (например, десять поднятых до двадцать второй мощности), но есть тем не менее, это затраты дополнительной памяти или вычислений при работе со всеми этими значениями "x" и "y", которые не нужны в SoE. END_EDIT_ADD2

Как и в моем первом ответе, сравнение говорит о том, что SoA теряет до очень колесо факторизованного SoE почти на всех фронтах, а именно:

  • Код SoA намного сложнее, и его сложнее записывать и обслуживать, тем сложнее реализовать сегментацию страниц, которая так важна для больших диапазонов, чтобы использовать максимальные возможности ЦП для скорости доступа к памяти.

  • SoA, вероятно, теряет циклы тактовых импульсов процессора не менее чем на 20% из-за немного более сложного внутреннего кода цикла и большего числа внешних циклов.

  • The SoA имеет немного больше операций по сравнению с высокооборотной факторизованной SoE (более высокая факторизация колес, чем в соответствии с вышеприведенным обсуждением) для достаточно большого диапазона от одного до четырех миллиардов.

  • SoA выигрывает за очень большие диапазоны, имея коэффициент улучшения log (log n), поскольку диапазоны становятся больше, но этот коэффициент позволил бы SoA догнать очень сильно факторизованный SoE с высоким колесом при очень высоком (возможно, около 10, поднятых до девятнадцатой степени), если это когда-либо, а затем только в том случае, если относительная сложность остается неизменной, что маловероятно.

Итак, я повторяю еще раз: " Зачем использовать сито Аткина, когда сито эратосфенов лучше почти во всех отношениях и особенно менее сложно, но при этом меньше накладных расходов вычислительного фактора?" Основываясь на моих выводах здесь, я чувствую, что SoA - это нижнее сито для любого разумного диапазона сит и, скорее всего, никогда не будет писать сегментированную версию сита Аткина на странице, но многие из них написаны на разных компьютерных языках для сита Эратосфен. Хотя сито Аткина интересно интеллектуально и математически, оно не очень практично, и сравнение Аткина и Бернштейна было ошибочным в чрезмерном ограничении колесной факторизации их реализации Сито Эратосфена, используемого в их сравнении, чтобы показать преимущество около 30%, что должно было на самом деле составлять около 60%, чем для постоянных накладных расходов на реализацию сита Аткина.