Самый быстрый простой тест для чисел малого размера - PullRequest
6 голосов
/ 21 сентября 2010

В свободное время я играю через проект Euler, и дошло до того, что мне нужно провести рефакторинг.Я реализовал Миллера-Рабина, а также несколько сит.Я слышал раньше, что сита на самом деле быстрее для небольших чисел, например, менее чем за несколько миллионов.У кого-нибудь есть информация по этому поводу?Google не очень помог.

Ответы [ 4 ]

9 голосов
/ 21 сентября 2010

Да, вы найдете в большинстве алгоритмов, которые вы можете обменять пространство на время. Другими словами, позволяя использовать больше памяти, скорость значительно увеличивается * a .

Я на самом деле не знаю алгоритм Миллера-Рабина, но, если он не будет проще, чем одно смещение влево / добавление и извлечение памяти, он будет сметен из воды предварительно рассчитанным сито.

Важная вещь здесь рассчитана заранее. Это хорошая идея с точки зрения производительности, чтобы предварительно рассчитать такие вещи, так как первый миллион простых чисел вряд ли изменится в ближайшем будущем: -)

Другими словами, создайте свое сито с чем-то вроде:

unsigned char primeTbl[] = {0,0,1,1,0,1,0,1,0,0,0,1};
#define isPrime(x) ((x < sizeof(primeTbl) ? primeTbl[x] : isPrimeFn(x))

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

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

Но, как и во всех вопросах оптимизации, измерьте, не угадайте!


* a Классическим случаем этого были некоторые триггерные функции, которые мне когда-то приходилось писать для встроенной системы. Это было конкурентное предложение по контракту, и у системы было немного больше памяти, чем у процессора.

Мы фактически выиграли контракт, так как наши контрольные показатели по функциям взорвали конкуренцию.

Почему? Потому что мы предварительно рассчитали значения в таблицу поиска, изначально рассчитанную на другом компьютере. Путем разумного использования редукции (понижение входных значений ниже 90 градусов) и свойств триггера (тот факт, что косинус - это просто фазовый сдвиг синуса, а остальные три квадранта связаны с первым), мы получили таблицу поиска до 180 записей (одна на полградуса).

Лучшие решения - это элегантные и хитрые: -)


Для чего стоит следующий код C сгенерирует такую ​​таблицу для вас, все простые числа меньше четырех миллионов (из них 283 000).

#include <stdio.h>

static unsigned char primeTbl[4000000];

int main (void) {
    int i, j;

    for (i = 0; i < sizeof(primeTbl); i++)
        primeTbl[i] = 1;

    primeTbl[0] = 0;
    primeTbl[1] = 0;
    for (i = 2; i < sizeof(primeTbl); i++)
        if (primeTbl[i])
            for (j = i + i; j < sizeof(primeTbl); j += i)
                primeTbl[j] = 0;

    printf ("static unsigned char primeTbl[] = {");
    for (i = 0; i < sizeof(primeTbl); i++) {
        if ((i % 50) == 0) {
            printf ("\n   ");
        }
        printf ("%d,", primeTbl[i]);
    }
    printf ("\n};\n");
    printf ("#define isPrime(x) "
        "((x < sizeof(primeTbl) ? primeTbl[x] : isPrimeFn(x))\n");

    return 0;
}

Если вы можете увеличить таблицу primeTbl до шестнадцати миллионов записей (16M), то обнаружите, что этого достаточно, чтобы число простых чисел превысило миллион (первые 1 031 130 простых чисел).

Теперь есть способы сделать так, чтобы они занимали меньше места, например, сохраняя нечетные числа и настраивая макрос, чтобы позаботиться об этом, или используя битовую маску вместо беззнаковых символов. Я сам предпочитаю простоту алгоритмов, если память доступна.

6 голосов
/ 21 сентября 2010

Рекомендую многоуровневый подход.Во-первых, убедитесь, что нет небольших простых факторов.Пробное деление на первые 20 или 30 простых чисел работает, хотя, если вы используете умный подход, вы можете уменьшить количество делений, необходимых с помощью gcds.Этот шаг отфильтровывает около 90% композитов.

Затем проверьте, является ли число сильным вероятным простым числом (тест Миллера-Рабина) для базы 2. Этот шаг удаляет почти все оставшиеся композиты, но некоторые редкие композитыможет пройти.

Последний шаг проверки зависит от того, насколько большим вы хотите пойти.Если вы хотите работать в небольшом диапазоне, выполните бинарный поиск по списку из 2-псевдослучайных чисел, вплоть до самого большого, который вы допускаете.Если это 2 ^ 32, в вашем списке будет только 10 403 члена, поэтому поиск должен принимать только 14 запросов.

Если вы хотите перейти к 2 ^ 64, теперь этого достаточно (благодаря работе Ян Фейситма ), чтобы проверить, является ли число псевдопраймером BPSW.(Вы также можете загрузить список всех исключений размером 3 ГБ, удалить те, которые будут удалены пробным разделением, и написать двоичный поиск на диске.) TR Приятно имеет хорошую страницу, объясняющую, как реализовать это достаточно эффективно.

Если вам нужно подняться выше, используйте описанный выше метод и используйте его в качестве подпрограммы для теста в стиле Поклингтона.Это растягивает определение «маленький»;если вам нужна дополнительная информация об этих методах, просто спросите.

2 голосов
/ 21 сентября 2010

Как вариант понятия предварительного вычисления, вы можете сначала дешево проверить, делится ли число кандидата p на 2, 3, 5, 7 или 11. Если нет, тогда объявите p, если простое число 2 р-1 = 1 (мод р). В какой-то момент это не удастся, но это работает до 100 миллионов, потому что я проверил это (предварительное вычисление).

Другими словами, все малозначные псевдопраймы Ферма к основанию 2 делятся на одно из 3, 5, 7 или 11.

РЕДАКТИРОВАТЬ:

Как правильно заметил @starblue, вышеприведенное просто неверно. У меня была ошибка в моей программе. Лучшее, что я могу сделать, это изменить вышеизложенное на:

Если кандидат p делится на 2, 3, 5, 7 или 11, объявить его составным;
Иначе, если p является одним из {4181921, 4469471, 5256091, 9006401, 9863461}, объявите его составным;
Иначе, если p пройдет тест Миллера-Рабина для оснований 2 и 5, тогда объявите его простым;
Остальное объявляю составным.

Это я проверял на целые числа меньше 10 000 000. Возможно, другая пара баз будет работать еще лучше.

Пожалуйста, примите мои извинения за мои ошибки.

РЕДАКТИРОВАТЬ 2:

Похоже, что информация, за которой я следовал, уже есть на странице Википедии для алгоритма Миллера-Рабина , раздел под названием "Детерминированные варианты теста" .

1 голос
/ 21 сентября 2010

Единственный способ - сравнить себя. Когда вы это сделаете, напишите это и опубликуйте где-нибудь в Интернете.

...