Китайская теорема об остатках

21

Теорема об остатках в Китае говорит нам, что мы всегда можем найти число, которое дает любые необходимые остатки при различных простых модулях. Ваша цель - написать код для вывода такого числа за полиномиальное время. Самый короткий код выигрывает.

Например, скажем, мы получили эти ограничения ( %представляет мод):

n % 7  == 2
n % 5  == 4
n % 11 == 0

Одним из решений является n=44. Первое ограничение выполняется потому 44 = 6*7 + 2, что и так 44имеет остаток 2при делении на 7, и, таким образом 44 % 7 == 2. Два других ограничения также выполнены. Существуют и другие решения, такие как n=814и n=-341.

вход

Непустой список пар (p_i,a_i), где каждый модуль p_iпредставляет собой отдельное простое число, а каждая цель a_i- натуральное число в диапазоне 0 <= a_i < p_i. Вы можете принять участие в любой удобной форме; это не обязательно должен быть список пар. Вы не можете предполагать, что вход отсортирован.

Выход

Целое число nтакое, что n % p_i == a_iдля каждого индекса i. Это не должно быть наименьшее такое значение, и может быть отрицательным.

Полиномиальное ограничение по времени

Чтобы предотвратить дешевые решения , которые просто пытаются n=0, n=1, n=2и так далее, ваш код должен работать в полиномиальное время в длине ввода . Обратите внимание, что число mво входных данных имеет длину Θ(log m), поэтому mсамо по себе оно не является полиномиальным по своей длине. Это означает, что вы не можете рассчитывать mили выполнять время операции m, но вы можете вычислять арифметические операции над значениями.

Вы не можете использовать неэффективный формат ввода, такой как унарный, чтобы обойти это.

Другие запреты

Не допускаются встроенные функции для выполнения следующих действий: Реализация теоремы китайского остатка, решение уравнений или факторных чисел.

Вы можете использовать встроенные модули для поиска модов и выполнения модульного сложения, вычитания, умножения и возведения в степень (с показателем натурального числа). Вы не можете использовать другие встроенные модульные операции, включая модульное обратное, деление и поиск заказа.

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

Они дают наименьшее неотрицательное решение. Ваш ответ может быть другим. Вероятно, лучше, если вы прямо проверите, что ваш вывод удовлетворяет каждому ограничению.

[(5, 3)] 
3

[(7, 2), (5, 4), (11, 0)]
44

[(5, 1), (73, 4), (59, 30), (701, 53), (139, 112)]
1770977011

[(982451653, 778102454), (452930477, 133039003)]
68121500720666070
XNOR
источник
Почему нет разделения?
jimmy23013
@ user23013 Модульное деление отсутствует, поскольку оно в основном модульное обратное.
xnor
Считается ли инверсия матрицы решением уравнений?
flawr
@flawr: я бы так подумал.
Алекс А.
@xnor: Что ты думаешь? А как насчет функций оптимизации?
flawr

Ответы:

9

Mathematica, 55 51 45

Модульное обратное запрещено, но допускается возведение в модуляр. По маленькой теореме Ферма n^(-1) % p == n^(p-2) % p.

(PowerMod[x=1##&@@#/#,#-2,#]x).#2&@@Thread@#&

Пример:

In[1]:= f = (PowerMod[x=1##&@@#/#,#-2,#]x).#2&@@Thread@#&;

In[2]:= f[{{5, 3}}]

Out[2]= 3

In[3]:= f[{{7, 2}, {5, 4}, {11, 0}}]

Out[3]= 1584

In[4]:= f[{{5, 1}, {73, 4}, {59, 30}, {701, 53}, {139, 112}}]

Out[4]= 142360350966

Просто для удовольствия:

ChineseRemainder@@Reverse@Thread@#&
alephalpha
источник
1
Вы можете сохранить один байт, поменяв местами порядок аргументов самой внутренней функции, так что вы можете использовать ее, PowerMod[#2,#-2,#]и я также не думаю, что для этой функции необходимо указывать имя, сводя ее к 48.
Martin Ender
Да, безымянные функции в порядке.
xnor
6

Python 2, 165 101 99 98 85 байт

Используя маленькую теорему Ферма, как и другие ответы. Не заботится о сохранении итоговой суммы в пределах модульного диапазона, так как нас не интересует самое маленькое решение. Спасибо за изменчивость за сохранение 13 байтов.

l=input();x=reduce(lambda a,b:a*b[0],l,1)
print sum(x/a*b*pow(x/a,a-2,a)for a,b in l)

[(5, 3)]
3
[(7, 2), (5, 4), (11, 0)]
1584
[(5, 1), (73, 4), (59, 30), (701, 53), (139, 112)]
142360350966
Ури Гранта
источник
1
Вы можете удалить пространство раньше for.
Исаак
1
x/a*b*pow(x/a,a-2,a)for a,b in lдолжно сработать.
Волатильность
Отличный момент! Я пытался избавиться от очевидной избыточности там, но забыл, что могу просто распаковать.
Ури Гранта
4

Pyth, 40 37 36 29

M*G.^G-H2Hsm*edg/u*GhHQ1hdhdQ

Использует маленькую теорему Ферма, благодаря алефальфе. Вычисляет, используя эту формулу .

orlp
источник
3

Руби, 129

Ну, товарищи, кажется, решения на Ruby должны быть длиннее, потому что модульное возведение в степень невозможно без загрузки библиотеки openssl и выполнения преобразований в OpenSSL :: BN. Тем не менее, было весело писать это:

require("openssl")
z=eval(gets)
x=1
z.map{|a,b|x*=a}
s=0
z.map{|a,b|e=a.to_bn;s+=(x/a).to_bn.mod_exp(e-2,e).to_i*b*x/a}
puts(s)
Atsby
источник
Вам не нужны скобки при вызове require, evalили puts.
Тутлеман
2

Python 2, 61

n=P=1
for p,a in input():n+=P*(a-n)*pow(P,p-2,p);P*=p
print n

Это использует вариацию конструкции продукта, которую используют другие ответы.

Идея состоит в том, чтобы зациклить ограничения и обновить решение, nчтобы соответствовать текущим ограничениям, не путая предыдущие. Для этого мы отслеживаем произведение Pпростых чисел, замеченных до сих пор, и наблюдаем, что добавление кратного Pне имеет эффекта по модулю любого уже видимого простого числа.

Итак, нам просто нужно изменить, nчтобы удовлетворить n%p == a, добавив правильное кратное P. Мы решаем за коэффициент c:

(n + P*c) % p == a

Это требует, чтобы c = (a-n) * P^(-1), где обратное взято по модулю p. Как отмечают другие, обратная задача может быть вычислена по маленькой теореме Ферма как P^(-1) = pow(P,p-2,p). Таким образом, c = (a-n) * pow(P,p-2,p)и мы обновляем nна n+= P * (a-n) * pow(P,p-2,p).

XNOR
источник
1

Haskell, 68 100 байт

f l=sum[p#(m-2)*n*p|(m,n)<-l,let a#0=1;a#n=(a#div n 2)^2*a^mod n 2`mod`m;p=product(map fst l)`div`m]

Использование: f [(5,1), (73,4), (59,30), (701,53), (139,112)]-> 142360350966.

Редактировать: теперь с быстрой «power / mod» функцией. Старая версия (68 байт) со встроенной функцией питания:

f l=sum[l#m^(m-2)`mod`m*n*l#m|(m,n)<-l]
l#m=product(map fst l)`div`m
Ними
источник
Я подозреваю, что ваша реализация power-mod не полиномиального времени, так как экспонент производит огромное количество перед модом. Вы пробовали последний контрольный пример?
xnor
@xnor: последний тестовый случай исчерпал память через несколько секунд на моем компьютере 2 ГБ. Я добавил функцию быстрого питания / мод.
Ними