Міцлер-Рабін Сильні псевдокрими


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першому першому номеру) .


1
Повідомлення в пісочниці (зараз видалено)
Mego

Чи дозволений алгоритм, який перевіряє все непарне значення від 1 до результату для сильної псевдопримітності?
користувач202729

@ user202729 Я не бачу, чому б цього не було. Що б змусило вас подумати, що це?
Мего

Я б запропонував зробити це питання з найшвидшим кодом, оскільки більшість відповідей буде просто грубою силою.
Ніл А.

@NeilA. Я не згоден, що це було б краще як найшвидший код. Хоча це правда, що відповіді майже напевно будуть грубою силою (оскільки інший алгоритм ще не розроблений, і я не очікую, що так стане PPCG), кодовий гольф набагато простіший, має набагато нижчий бар'єр для вступу (оскільки подачі може скласти свої власні рішення), не вимагає від мене запускати та забивати кожне рішення (і мати справу з надмірними умовами виконання), і проблема є досить цікавою, як проблема з гольфом.
Мего

Відповіді:


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);}

Бере вхід на основі stdin на основі 1, наприклад:

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 байти; це виявляється напрочуд конкурентоспроможним, враховуючи, що це дві різні мови для гольфу: 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, як відомо, повільно порівняно з іншими мовами.

Визначає пробний поділ на абсолютну коректність, потім повторно застосовує тест Міллера-Рабіна, поки не буде знайдена псевдокрима.

Спробуйте в Інтернеті!

EDIT : Нарешті golfed відповідь

EDIT : Використовується minдля перевірки первинності пробного поділу та змінив його на a lambda. Менш ефективний, але коротший. Також я не міг допомогти собі і використав декілька побітових операторів (різниці в довжині). Теоретично це повинно працювати (трохи) швидше.

EDIT : Дякую @Dave. Мій редактор навчався мене. Я думав, що я використовую вкладки, але він замість цього перетворюється на 4 пробіли. Крім того, пройшов майже кожен наконечник Python і застосував його.

EDIT : Переключений на 0-індексацію, дозволяє мені зберегти пару байтів з генерацією прайменів. Також переосмислив пару порівнянь

EDIT : Використовувала змінну для зберігання результатів тестів замість for/elseоператорів.

EDIT : переміщено lambdaвнутрішню функцію, щоб усунути необхідність параметра.

EDIT : Перетворено на програму для збереження байтів

EDIT : 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$_}{'
Дада

Я про це зовсім забув }{. (Як не дивно, я згадав про дужки з дужкою, але минув час, коли я займався гольфом в Перлі і не міг згадати правила, як його випустити.) Я взагалі не знав про ntheoryабревіатуру.
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.