Квадратный корень из числа


13

Задача состоит в следующем: учитывая положительное целое число xи простое число n > x, выведите наименьшее положительное целое число, yтакое что (y * y) mod n = x. Важной частью этого вопроса является срок, указанный ниже, который исключает грубые решения.

Если такого значения нет, yваш код должен выводиться N.

Контрольные примеры

(2, 5, N), 
(3, 5, N), 
(4, 5, 2),
(524291, 1048583, N),
(529533, 1048583, N),
(534775, 1048583, 436853),
(540017, 1048583, 73675),
(536870913, 1073741827, 375394238),
(542239622, 1073741827, 267746399),
(547608331, 1073741827, N),
(552977040, 1073741827, 104595351),
(1099511627676, 1099511627791, N),
(1099511627677, 1099511627791, 269691261521),
(1099511627678, 1099511627791, 413834069585),
(1267650600228229401496703204376, 1267650600228229401496703205653, 5312823546347991512233563776),
(1267650600228229401496703204476, 1267650600228229401496703205653, N)
(1267650600228229401496703204576, 1267650600228229401496703205653, N)
(1267650600228229401496703204676, 1267650600228229401496703205653, 79905476259539917812907012931)

Вход и выход

Вы можете принимать и выводить данные любым удобным для вас способом. Если вам не нравится вывод, Nтогда Falseyподойдет любое значение.

ограничения

Ваш код должен отвечать на все тесты менее чем за 1 минуту на стандартном рабочем столе. Это ограничение по времени только для предотвращения грубых ответов, и я ожидаю, что хорошие ответы начнутся практически мгновенно. Вы не можете использовать какую-либо библиотеку или встроенную программу, которая решает эту проблему или проверяет, является ли число квадратичным остатком.


2
Таким образом, языки без поддержки целочисленных типов данных исключаются. Жаль
Луис Мендо

1
@LuisMendo Нет, если вы можете кодировать поддержку для 1267650600228229401496703205653себя или если у вас есть поддержка 128 бит, например, __int128в gcc. Существует также ряд 256-битных библиотек int для различных языков. Наконец, многие языки имеют произвольную точность в библиотеке.

Ответы:


7

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принимает его первый член, минимум.


11

Python 2, 166 байт

def Q(x,n,a=0):
 e=n/2
 while pow(a*a-x,e,n)<2:a+=1
 w=a*a-x;b=r=a;c=s=1
 while e:
    if e%2:r,s=(r*b+s*c*w)%n,r*c+s*b
    b,c=(b*b+c*c*w)%n,2*b*c;e/=2
 return min(r,-r%n)

%timeit Q(1267650600228229401496703204676,1267650600228229401496703205653) 100 loops, best of 3: 2.83 ms per loop :)

3
Какой отличный ответ! Вы восстановили мою веру в PPCG.

5
Извините за вопрос новичка, но что такое PPCG? Польская группа Python Coders?
Обратный инженер

@DaveBoltman Программирование головоломок и Code Golf.
17


3

Haskell , 326 байт

Обычно мне нравятся грубые ответы. Поскольку это сильно обескураживает из-за ограничения по времени, вот самый эффективный способ, который я знаю:

r p=((\r->min(mod(-r)p)r)$).f p
f p x|p==2=x|q x=f 0 0|mod p 4==3=x&div(p+1)4|let(r,s)=foldl i(p-1,0)[1..t 1o]=m$x&(d$r+1)*(b&d s)where q a=1/=a&o;m=(`mod`p);a&0=1;a&e|even e=m$a&d e^2|0<1=m$(a&(e-1))*a;b=[n|n<-[2..],q n]!!0;i(a,b)_|m(x&d a*b&d b)==p-1=(d a,d b+o)|0<1=(d a,d b);o=d p;t y x|even x=t(y+1)(d x)|0<1=y;d=(`div`2)

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

Я уверен, что это может быть продолжено, но это должно сделать пока.


Не могли бы вы изменить код TIO, чтобы он выдавал ответы в виде вывода? Я просто получаю "True" в данный момент.


@Lembik Вам нужно заменить testCasesна те из оригинального TIO, которые едва ли вписываются в комментарий даже без них.
Эрджан Йохансен

@ ØrjanJohansen Большое спасибо! Я скорректировал свой ответ с вашим кодом и заменил testCases.
ბიმო

Да, я вижу странную ошибку с этой ссылкой TIO - если я нажимаю на нее, она имеет код, но не работает и не получает URL из опций меню - но если я скопирую URL из адресной строки и вставлю его в Вкладка другая, тогда все работает.
Орджан Йохансен
Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.