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
бере його першого члена, як мінімум.