Pyth, 83 82 байт
=eAQM.^GHQKf%=/H=2;1=gftgT/Q;1HJg~gGHh/H2WtG=*J=gT^2t-K=Kfq1gG^2T1=%*G=^T2Q;hS%_BJ
Тестовий набір
Ця програма реалізує алгоритм Tonelli-Shanks . Я написав це, уважно переглядаючи сторінку Вікіпедії. Це приймається як вхід (n, p).
Про відсутність квадратного кореня повідомляється наступною помилкою:
TypeError: pow() 3rd argument not allowed unless all arguments are integers
Це дуже хитромудрий код для гольфу, написаний в імперативному стилі, на відміну від більш поширеного функціонального стилю Pyth.
Один найтонший аспект Pyth, який я використовую, це те =, що якщо за ним не одразу слідує змінна, шукає в програмі наступну змінну вперед, а потім присвоює цій змінній результат наступного виразу, а потім повертає цей результат. Я буду посилатися у всьому поясненні на сторінку вікіпедії: Алгоритм Тонеллі-Шенкс , як це алгоритм, який я реалізую.
Пояснення:
=eAQ
Aприймає 2-кортеж як вхід і присвоює значення відповідно Gі, Hвідповідно, повертає свої дані. Q- початковий вхід. eповертає останній елемент послідовності. Після цього фрагмента коду, Gце nі Hта Qє p.
M.^GHQ
Mвизначає функцію введення 2 g, де входи є Gі H. .^- швидка модульна функція експоненції Піта. Цей фрагмент визначає gяк модус експоненції Q.
Kf%=/H=2;1
fвизначає повторення до помилкового циклу і повертає кількість ітерацій, для яких він працює, з урахуванням 1його введення. Під час кожної ітерації циклу ділимо Hна 2, встановлюємо Hце значення і перевіряємо, чи результат непарний. Як тільки це є, ми зупиняємось. Kзберігає кількість повторень, які це взяло.
Одна дуже хитра річ - це =2;біт. =заздалегідь шукає наступну змінну, яка є T, так Tвстановлено 2. Однак Tвсередині fциклу є лічильник ітерації, тому ми використовуємо ;для отримання значення Tз глобального середовища. Це робиться для того, щоб зберегти пару байтів пробілу, які в іншому випадку знадобляться для розділення чисел.
Після цього фрагмента, Kце Sз вікіпедії статті (вікі), і Hце Qз вікі, і Tє 2.
=gftgT/Q;1H
Тепер нам потрібно знайти квадратичний нерезидуальний мод p. Ми будемо грубо застосовувати це за допомогою критерію Ейлера. /Q2є (p-1)/2, оскільки /є floored поділ, тому ftgT/Q;1знаходить перше ціле число Tде T ^ ((p-1)/2) != 1, як бажано. Нагадаємо, що ;знову тягнеться Tіз глобального середовища, якого досі є 2. Цей результат - zіз вікі.
Далі, щоб створити cз вікі, нам потрібно z^Q, тому ми загортаємо все вище g ... Hта призначаємо результат T. Тепер Tце cз вікі.
Jg~gGHh/H2
Давайте виділимо це: ~gGH. ~є як =, але повертає початкове значення змінної, а не її нове значення. Таким чином, вона повертається G, що nз вікі.
Це призначає Jзначення n^((Q+1)/2), яке є Rз вікі.
Тепер набирає чинності наступне:
~gGH
Це призначає Gзначення n^Q, яке є tз вікі.
Тепер у нас створені наші змінні циклу. M, c, t, Rз вікі є K, T, G, J.
Тіло циклу складне, тому я збираюся представити його з пробілом, як я його написав:
WtG
=*J
=
gT^2
t-
K
=Kfq1gG^2T1
=%*G=^T2Q;
Спочатку перевіряємо, чи Gє 1. Якщо так, виходимо з циклу.
Наступний код, який працює:
=Kfq1gG^2T1
Тут ми шукаємо перше значення iтакого, що G^(2^i) mod Q = 1, починаючи з 1. Результат зберігається в K.
=gT^2t-K=Kfq1gG^2T1
Тут ми беремо старе значення K, віднімаємо нове значення K, віднімаємо 1, піднімаємо 2 до цієї потужності, а потім піднімаємо Tдо цієї модної потужності Q, а потім присвоюємо результат T. Це Tзрівняється bз вікі.
Це також рядок, який закінчує цикл і відмовляється, якщо немає рішення, оскільки в цьому випадку нове значення Kбуде рівним старому значенню K, 2 буде підняте до -1, і модульна експоненція призведе до помилки.
=*J
Далі ми множимо Jна вищенаведений результат і зберігаємо його назад J, постійно Rоновлюючи.
=^T2
Потім ми Tповертаємо квадрат і зберігаємо результат назад T, Tповертаючись до cвікі.
=%*G=^T2Q
Потім ми множимо Gна цей результат, беремо його мод Qі зберігаємо результат назад в G.
;
І закінчуємо цикл.
Після закінчення циклу J- квадратний корінь nмода p. Щоб знайти найменший, ми використовуємо такий код:
hS%_BJ
_BJстворює список Jта його заперечення, %неявно приймає Qза свій другий аргумент і використовує поведінку Pyth за замовчуванням для застосування % ... Qдо кожного члена послідовності. Потім Sсортує список і hбере його першого члена, як мінімум.