Pyth, 83 82 байта
=eAQM.^GHQKf%=/H=2;1=gftgT/Q;1HJg~gGHh/H2WtG=*J=gT^2t-K=Kfq1gG^2T1=%*G=^T2Q;hS%_BJ
Тестирование
Эта программа реализует алгоритм Тонелли-Шанкса . Я написал это, внимательно следя за страницей Википедии. Это берет как вход (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определяет цикл повторения до false и возвращает количество итераций, для которых он выполняется, в 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, так как /это деление по полам, поэтому 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принимает его первый член, минимум.