Миллер-Рабин Сильные Псевдопричины

16

Если задано неотрицательное целое число N, выведите наименьшее нечетное положительное целое число, являющееся сильным псевдослучестным, для всех первых Nпростых оснований.

Это последовательность OEIS A014233 .

Тестовые случаи (одноиндексированные)

1       2047
2       1373653
3       25326001
4       3215031751
5       2152302898747
6       3474749660383
7       341550071728321
8       341550071728321
9       3825123056546413051
10      3825123056546413051
11      3825123056546413051
12      318665857834031151167461
13      3317044064679887385961981

Контрольные примеры для N > 13недоступны, поскольку эти значения еще не найдены. Если вам удастся найти следующие термины в последовательности, обязательно отправьте их / их в OEIS!

правила

  • Вы можете выбрать значение Nс нулевым или одним индексом.
  • Для вашего решения приемлемо работать только для значений, представляемых в целочисленном диапазоне вашего языка (например, N = 12для 64-разрядных целых чисел без знака), но ваше решение теоретически должно работать для любого ввода с допущением, что ваш язык поддерживает целые числа произвольной длины.

Фон

Любое положительное четное целое число xможет быть записано в форме, x = d*2^sгде dнечетно. dи sможет быть вычислено путем многократного деления nна 2 до тех пор, пока частное не перестанет делиться на 2. dЯвляется ли это конечное частное и sчислом, когда 2 делит n.

Если натуральное число nявляется простым, то маленькая теорема Ферма утверждает:

Ферма

В любом конечном поле Z/pZ (где pесть простое число) единственными квадратными корнями 1являются 1и -1(или, что эквивалентно, 1и p-1).

Мы можем использовать эти три факта, чтобы доказать, что одно из следующих двух утверждений должно быть истинным для простого числа n(где d*2^s = n-1и rявляется некоторым целым числом [0, s)):

Условия Миллера-Рабина

Тест на примитивность Миллера-Рабина работает путем проверки контрапозитива по приведенному выше утверждению: если существует основание a, в котором оба вышеуказанных условия являются ложными, то nэто не простое число. Эта база aназывается свидетелем .

Теперь тестирование каждой базы [1, n)было бы непомерно дорогим по времени вычислений для больших n. Существует вероятностный вариант теста Миллера-Рабина, который проверяет только некоторые случайно выбранные базы в конечном поле. Однако было обнаружено, что тестирование только простых aоснований является достаточным, и, таким образом, тест может быть выполнен эффективным и детерминистическим способом. В действительности, не все основные основания должны быть проверены - требуется только определенное число, и это число зависит от размера значения, проверяемого на первичность.

Если тестируется недостаточное количество простых базисов, тест может давать ложноположительные значения - нечетные составные целые числа, когда тест не может доказать их составность. В частности, если основание aне может доказать композицию нечетного составного числа, это число называется сильным псевдослучайным к основанию a. Эта задача состоит в том, чтобы найти нечетные составные числа, которые являются сильными псевдопримерами для всех оснований, меньших или равными первому Nчислу (что эквивалентно тому, чтобы сказать, что они являются сильными псевдопримями для всех простых оснований, меньших или равными Nпервому числу) ,

Mego
источник
1
Сообщение песочницы (сейчас удалено)
Mego
Допускается ли в соответствии с правилами алгоритм, который проверяет все нечетные значения от 1 до результирующего на предмет сильной псевдопримальности?
user202729
@ user202729 Я не понимаю, почему это не так. Что заставит вас думать, что это так?
Мего
Я бы предложил сделать этот вопрос с быстрым кодом, потому что большинство ответов будут просто грубой силой.
Нил А.
@NeilA. Я не согласен, что это будет лучше, чем самый быстрый код. Хотя это правда, что ответы почти наверняка будут грубой силой (поскольку еще один алгоритм еще не разработан, и я не ожидаю, что PPCG сделает это), Code Golf намного проще, имеет гораздо более низкий барьер для входа (так как отправители может забрать свои собственные решения), не требует, чтобы я запускал и оценивал каждое решение (и имел дело с непомерными временами выполнения), и проблема достаточно интересна как вызов гольфа.
Mego

Ответы:

4

C 349 295 277 267 255 байт

N,i;__int128 n=2,b,o,l[999];P(m){i<N&&P(m<2?l[i++]=n:n%m?m-1:n++);}main(r){for(P(scanf("%d",&N));r|!b;)for(++n,b=i=N;i--&&b;){for(b=n-1,r=0;~b&1;b/=2)++r;for(o=1;b--;o=o*l[i]%n);for(b=o==1;r--;o=o*o%n)b|=o>n-2;for(o=r=1;++o<n;r&=n%o>0);}printf("%llu",n);}

Принимает ввод на основе 1 в stdin, например:

echo "1" | ./millerRabin

Конечно, в ближайшее время он не обнаружит никаких новых значений в последовательности, но он выполняет свою работу. ОБНОВЛЕНИЕ: теперь еще медленнее!

  • Чуть быстрее и короче, с вдохновением от ответа Нила А ( a^(d*2^r) == (a^d)^(2^r))
  • Значительно медленнее снова после осознания того, что все решения этой задачи будут нечетными, поэтому нет необходимости явно предписывать нам проверять только нечетные числа.
  • Теперь с помощью GCC __int128, который короче, чем unsigned long longпри работе с большими числами! Также на машинах с прямым порядком байтов printf с %lluвсе еще работает отлично.

Менее уменьшенная

N,i;
__int128 n=2,b,o,l[999];
P(m){i<N&&P(m<2?l[i++]=n:n%m?m-1:n++);}
main(r){
    for(P(scanf("%d",&N));r|!b;)
        for(++n,b=i=N;i--&&b;){
            for(b=n-1,r=0;~b&1;b/=2)++r;
            for(o=1;b--;o=o*l[i]%n);
            for(b=o==1;r--;o=o*o%n)b|=o>n-2;
            for(o=r=1;++o<n;r&=n%o>0);
        }
    printf("%llu",n);
}

(Устаревшее) Разбивка

unsigned long long                  // Use the longest type available
n=2,N,b,i,d,p,o,                    // Globals (mostly share names with question)
l[999];                             // Primes to check (limited to 999, but if you
                                    // want it to be "unlimited", change to -1u)
m(){for(o=1;p--;o=o*l[i]%n);}       // Inefficiently calculates (l[i]^p) mod n

// I cannot possibly take credit for this amazing prime finder;
// See /codegolf//a/5818/8927
P(m){i<N&&P(m<2?l[i++]=n:n%m?m-1:n++);}

main(r){
    for(
        P(scanf("%llu",&N));        // Read & calculate desired number of primes
        r|!b;){                     // While we haven't got an answer:
        n=n+1|1;                    // Advance to next odd number
        for(b=1,i=N;i--&&b;){       // For each prime...
            for(d=n-1,r=0;~d&1;d/=2)++r; // Calculate d and r: d*2^r = n-1
            // Check if there exists any r such that a^(d*2^r) == -1 mod n
            // OR a^d == 1 mod n
            m(p=d);
            for(b=o==1;r--;b|=o==n-1)m(p=d<<r);
            // If neither of these exist, we have proven n is not prime,
            // and the outer loop will keep going (!b)
        }
        // Check if n is actually prime;
        // if it is, the outer loop will keep going (r)
        for(i=r=1;++i<n;r&=n%i!=0);
    }
    printf("%llu",n);               // We found a non-prime; print it & stop.
}

Как уже упоминалось, это использует ввод на основе 1. Но для n = 0 он выдает 9, что соответствует связанной последовательности https://oeis.org/A006945 . Уже нет; сейчас висит на 0.

Должно работать для всех n (по крайней мере, пока выход не достигнет 2 ^ 64), но невероятно медленно. Я проверил это на n = 0, n = 1 и (после долгого ожидания), n = 2.

Дейв
источник
Я делаю прорыв в своем решении, а потом ты просто один на один ... Отлично!
Нил А.
@NeilA. Сожалею! Я играл с более короткими типами int, прежде чем вы опубликовали свое обновление. Я уверен, что вы найдете где-нибудь 2 байта; это оказывается на удивление конкурентоспособным, учитывая, что это 2 разных языка, не связанных с гольфом: D
Дэйв
3

Python 2, 633 465 435 292 282 275 256 247 байт

0 индексированные

Поставьте под сомнение вашу реализацию и попробуйте что-то новое

Преобразование из функции в программу как-то экономит несколько байтов ...

Если Python 2 дает мне более короткий способ сделать то же самое, я собираюсь использовать Python 2. Деление по умолчанию является целочисленным, так что более простой способ деления на 2 и printне требует скобок.

n=input()
f=i=3
z={2}
a=lambda:min([i%k for k in range(2,i)])
while n:
 if a():z|={i};n-=1
 i+=2
while f:
 i+=2;d,s,f=~-i,0,a()
 while~d&1:d/=2;s+=1
 for y in z:
  x=y**d%i
  if x-1:
   for _ in[]*s:
    x*=x
    if~x%i<1:break
   else:f=1
print i

Попробуйте онлайн!

Python известен своей медлительностью по сравнению с другими языками.

Определяет пробное деление теста на абсолютную корректность, затем многократно применяет критерий Миллера-Рабина до тех пор, пока не будет найден псевдопригодность.

Попробуйте онлайн!

РЕДАКТИРОВАТЬ : наконец-то игра в гольф ответ

РЕДАКТИРОВАТЬ : Используется minдля теста первичности пробного деления и изменил его на a lambda. Менее эффективно, но короче. Также не мог помочь себе и использовал пару побитовых операторов (без разницы в длине). В теории это должно работать (немного) быстрее.

РЕДАКТИРОВАТЬ : Спасибо @Dave. Мой редактор контролировал меня. Я думал, что использую вкладки, но вместо этого он конвертируется в 4 пробела. Также изучил практически все советы по Python и применил их.

РЕДАКТИРОВАТЬ : Переключен на 0-индексирование, позволяет мне сохранить пару байтов с генерацией простых чисел. Также переосмыслил пару сравнений

РЕДАКТИРОВАТЬ : Использовать переменную для хранения результатов тестов вместо for/elseоператоров.

РЕДАКТИРОВАТЬ : переместил lambdaвнутрь функции, чтобы устранить необходимость в параметре.

РЕДАКТИРОВАТЬ : преобразован в программу для сохранения байтов

РЕДАКТИРОВАТЬ : Python 2 экономит мне байты! Также мне не нужно конвертировать входные данные вint

Нил А.
источник
+1 за то, как ты справился a^(d*2^r) mod n!
Дэйв
Знаете ли вы, что вы можете использовать отступы в один пробел (или одну вкладку) в Python, чтобы сохранить… много байтов, на самом деле
Дэйв
@Dave: это использует 1 вкладку на уровень отступа
Нил А.
Я думаю, что ваша IDE мешает вам и экономит место, сообщая, что использует вкладки; когда я заменяю их на отдельные пробелы, я получаю количество байтов всего 311 байтов! Попробуйте онлайн!
Дэйв
@Dave: Хорошо, это странно, спасибо, я обновлю ответ.
Нил А.
2

Perl + Math :: Prime :: Util, 81 + 27 = 108 байт

1 until!is_provable_prime(++$\)&&is_strong_pseudoprime($\,2..nth_prime($_));$_=""

Запуск с -lpMMath::Prime::Util=:all(штраф 27 байт, ой).

объяснение

Это не просто Mathematica, которая имеет встроенную функцию для чего угодно. В Perl есть CPAN, одно из первых больших библиотечных хранилищ, в котором есть огромная коллекция готовых решений для таких задач, как эта. К сожалению, они не импортируются (или даже не устанавливаются) по умолчанию, а это означает, что использовать их в , но когда один из них идеально подходит для решения проблемы…

Мы пробегаем последовательные целые числа, пока не найдем то, которое не является простым, и все же сильным псевдопригодным для всех целочисленных оснований от 2 до n- го простого числа. Опция командной строки импортирует библиотеку, содержащую рассматриваемые встроенные функции, а также задает неявный ввод (для строки за раз; Math::Prime::Utilимеет собственную встроенную библиотеку bignum, которая не любит переводы строк в свои целые числа). При этом $\в качестве переменной используется стандартный прием Perl (разделитель выходных строк) в качестве переменной, чтобы уменьшить неуклюжие разборы и позволить неявно генерировать выходные данные.

Обратите внимание, что нам нужно использовать is_provable_primeздесь, чтобы запросить детерминированный, а не вероятностный, простой тест. (Особенно с учетом того, что вероятностный простой тест, скорее всего, использует Миллера-Рабина, что в этом случае не может дать надежных результатов!)

Perl + Math :: Prime :: Util, 71 + 17 = 88 байт, в сотрудничестве с @Dada

1until!is_provable_prime(++$\)&is_strong_pseudoprime$\,2..n‌​th_prime$_}{

Выполнить с -lpMntheory=:all(штраф 17 байт).

При этом используются несколько уловок игры в гольф на Perl, о которых я либо не знал (по-видимому, у Math :: Prime :: Util есть аббревиатура!), О которых знал, но не думал об использовании ( }{чтобы выводить $\неявно один раз, а не "$_$\"неявно каждую строку) или знал, но каким-то образом сумел неправильно (удалив скобки из вызовов функций). Спасибо @Dada за указание на это. Кроме того, он идентичен.


источник
Конечно, язык гольфа приходит и побеждает остальных. Отлично сработано!
Нил А.
Вы можете использовать ntheoryвместо Math::Prime::Util. Кроме того, }{вместо ;$_=""должно быть хорошо. И вы можете опустить пробел после 1и скобки нескольких вызовов функций. Также &работает вместо &&. Это должно дать 88 байт:perl -Mntheory=:all -lpe '1until!is_provable_prime(++$\)&is_strong_pseudoprime$\,2..nth_prime$_}{'
Дада
Я полностью забыл о }{. (Как ни странно, я вспомнил про скобки, но я давно играл в гольф на Perl и не мог вспомнить правила его пропуска.) ntheoryХотя я совсем не знал об аббревиатуре.