поиск A^b^c^ ... mod m
Я хотел бы расчета:
abcd... mod m
знаете ли вы какой-либо эффективный способ , так как это число слишком велико , но a, b, c,... и m вписывается в простой 32-битный int.
Какие Идеи?
предостережение: этот вопрос отличается от поискаb mod m.
Также обратите внимание, что аbc не совпадает (аb)c. Последнее равно abc. Возведение в степень является правоассоциативным.
6 ответов
ответ не содержит полного формального математического доказательства правильности. Я предположил, что здесь в этом нет необходимости. Кроме того, это было бы очень неразборчиво на SO (нет MathJax, например).
Я буду использовать (немного) specific премьер-факторизации. Это не лучший вариант, но достаточно.
tl; dr
мы хотим вычислить a^x mod m
. Мы будем использовать функцию modpow(a,x,m)
. Описанный под.
- если
x
достаточно мал (не экспоненциальная форма или существуетp^x | m
) просто вычислить его и вернуть - разделить на простые числа и вычислить
p^x mod m
отдельно для каждого штриха, используя Эйлера теорема. Цитата из Википедии:Эйлера теорема:
Еслиn
иa
являются coprime положительные целые числа, тогда где φ (n) -Эйлера.предположение
numbers are co-prime
очень важно, как Nabb показывает в комментарий. Так, во-первых, мы должны убедиться, что эти числа взаимно простые. (Для большей ясности предположимx = b^(c^...)
.), Потому что , где мы можем разложить на множителиa
, и отдельно посчитатьq1 = (p1^alpha)^x mod m,q2 = (p2^beta)^x mod m...
и затем вычислить ответ в простой способ(q1 * q2 * q3 * ... mod m)
. Номер имеет самое большееo(log a)
prime factors, поэтому мы будем вынуждены выполнять не болееo(log a)
расчетов.на самом деле нам не нужно разделяться на каждый простой фактор
a
(если не у всех случаются вm
с другими экспонентами), и мы можем комбинировать с тем же показателем, но это не примечательно сейчас.теперь взгляните на на
Θ(log c)
времени. И после (используя тот же алгоритм)a^x' mod m
, что равно решению.если
x = b^(c^(d^...)
мы решим его рекурсивно. Во-первых вычислитьt1 = totient(m)
, послеt2 = totient(t1)
и так далее. Например,x=b^(c^d)
. Еслиt1=totient(m)
,a^x mod m = a^(b^(c^d) mod t1)
, и мы можем сказатьb^(c^d) mod t1 = b^(c^d mod t2) mod t1
, гдеt2 = totient(t1)
. все, что мы вычисляем, используя возведение в степень по квадрату алгоритма. Примечание: если некоторый тотиент не является со-простым к экспоненте, необходимо использовать тот же трюк, что и в основной задаче (на самом деле, мы должны забыть, что это экспонента и рекурсивно решить проблему, как в основной задаче). В приведенном выше примере, еслиt2
не является относительно простым с c, мы должны использовать этот трюк.вычислить
φ(n)
обратите внимание простой факты:
- если
gcd(a,b)=1
, потомφ(ab) = φ(a)*φ(b)
- если
p
это primeφ(p^k)=(p-1)*p^(k-1)
поэтому мы можем факторизовать
n
(ak.n = p1^k1 * p2^k2 * ...
) и отдельно посчитатьφ(p1^k1),φ(p2^k2),...
, используя факт 2. Затем объедините это, используя факт 1.φ(n)=φ(p1^k1)*φ(p2^k2)*...
стоит помнить, что, если мы будем вычислять totient повторно, мы можем использовать сито Эратосфена и сохранить простые числа в таблице. Это позволит снизить постоянный.
python пример: (это правильно, по той же причине, как этот алгоритм факторизации)
def totient(n) : # n - unsigned int result = 1 p = 2 #prime numbers - 'iterator' while p**2 <= n : if(n%p == 0) : # * (p-1) result *= (p-1) n /= p while(n%p == 0) : # * p^(k-1) result *= p n /= p p += 1 if n != 1 : result *= (n-1) return result # in O(sqrt(n))
корпус:
a
b
c
mod m
потому что на самом деле он делает то же самое много раз, я считаю, что этот случай покажет вам, как решить это в целом.
Во-первых, мы должны разделитьa
в расцвете сил. Лучший представление будет pair<number, exponent>
.
c++11 пример:std::vector<std::tuple<unsigned, unsigned>> split(unsigned n) { std::vector<std::tuple<unsigned, unsigned>> result; for(unsigned p = 2; p*p <= n; ++p) { unsigned current = 0; while(n % p == 0) { current += 1; n /= p; } if(current != 0) result.emplace_back(p, current); } if(n != 1) result.emplace_back(n, 1); return result; }
после разделения, мы должны рассчитать
(p^z)^(b^c) mod m=p^(z*(b^c)) mod m
для каждой пары. Во-первых, мы должны проверить, еслиp^(z*(b^c)) | m
. Если да, то ответ справедлив (p^z)^(b^c), но это возможно только в том случае, еслиz,b,c
очень маленькие. Я считаю, что мне не нужно показывать ему пример кода.
И, наконец, еслиp^(z*b^c) > m
мы должны вычислить ответ. Во-первых, мы должны вычислитьc' = gcd(m, p^(z*b^c))
. После того, как мы сможем вычислитьt = totient(m')
. и(z*b^c - c' mod t)
. Это простой способ получить ответ.function modpow(p, z, b, c, m : integers) # (p^z)^(b^c) mod m c' = 0 m' = m while m' % p == 0 : c' += 1 m' /= p # now m' = m / gcd((p^z)^(b^c), m) t = totient(m') exponent = z*(b^c)-c' mod t return p^c' * (p^exponent mod m')
и ниже Python работающего пример:
def modpow(p, z, b, c, m) : # (p^z)^(b^c) mod m cp = 0 while m % p == 0 : cp += 1 m /= p # m = m' now t = totient(m) exponent = ((pow(b,c,t)*z)%t + t - (cp%t))%t # exponent = z*(b^c)-cp mod t return pow(p, cp)*pow(p, exponent, m)
используя эту функцию, мы можем легко вычислить
(p^z)^(b^c) mod m
, после того, как мы просто несколько все результаты (mod m
), мы также можем рассчитать все на постоянной основе. Пример ниже. (Надеюсь, я не ошибся, когда писал.) Достаточно велики только предположения, b, c (b^c > log(m)
Ак. каждыйp^(z*b^k)
не делитm
), это простая проверка, и я не вижу смысла делать беспорядок ею.def solve(a,b,c,m) : # split and solve result = 1 p = 2 # primes while p**2 <= a : z = 0 while a % p == 0 : # calculate z a /= p z += 1 if z != 0 : result *= modpow(p,z,b,c,m) result %= m p += 1 if a != 1 : # Possible last prime result *= modpow(a, 1, b, c, m) return result % m
похоже, это работает.
демо и это правильно! - если
abc mod m = abc mod n mod m, где n = φ (m) Эйлера.
Если M простое, то n = m-1.
Edit: как указал Nabb, это выполняется только в том случае, если a является coprime к m. Так что сначала вы должны это проверить.
модульная Экспоненциальность-правильный способ решить эту проблему, вот немного подсказки:
Найтиbcd % m
Вы должны начать с расчета
в % м, тоb % m, затем abc % m, а затем abcd % м ... (вы поняли идею)
найтиb % m, вам в основном нужны две идеи: [пусть B=этаж (b/2)]
(% = ультрасовременный)
таким образом,
если b даже
аb % m = (aB % m)2 % m
или если b нечетное
аb % m = (((aB % м)2) * (В % К)) % М
Итак, если бы вы знали значениеB, вы можете вычислить это значение.
НайтиB, примените аналогичный подход, разделив B, пока не достигнете 1.
например, для расчета 1613 % 11:
1613 % 11 = (16 % 11)13 % 11 = 513 % 11
= (56 % 11) * (56 % 11) * (5 % 11)
Чтобы найти 56 % 11:
56 % 11 = ((53 % 11) * (53 % 11)) % 11
Чтобы найти 53%11:
53 % 11 = ((51 % 11) * (51 % 11) * (5 % 11)) % 11
= (((5 * 5) % 11) * 5) % 11 = ((25 % 11) * 5) % 11 = (3 * 5) % 11 = 15 % 11 = 4
Подключение этого значения к (II) дает
56 % 11 = (((4 * 4) % 11) * 5) % 11 = ((16 % 11) * 5) % 11 = (5 * 5) % 11 = 25 % 11 = 3
Подключение этого значения к (I) дает
513 % 11 = ((3 % 11) * (3 % 11) * 5) % 11 = ((9 % 11) * 5) % 11 = 45 % 11 = 4
таким образом 513 % 11 = 4
С помощью этого вы можете вычислить что-нибудь из формы a513 % 11 и так далее...
так как для любых отношений
a=x^y
, отношение инвариантно по отношению к используемой вами числовой базе (база 2, База 6, база 16 и т. д.).поскольку операция mod n эквивалентна извлечению наименее значимой цифры (LSD) в базе N
так как на ЛСД результата а в базе N может влиять только ЛСД X в базе N, а не цифры в более высоких местах. (например, 34*56 = 30*50+30*6+50*4+4*5 = 10*(3+50+3*6+5*4)+4*6)
LSD(A)=LSD(X^Y)
мы можем сделать вывод
LSD(A)=LSD(LSD(X)^Y)
A mod N = ((X mod N) ^ Y) mod N
и
(X ^ Y) mod N = ((X mod N) ^ Y) mod N)
поэтому вы можете сделать мод перед каждым шагом мощности, который сохраняет ваш результат в диапазоне целых чисел.
это предполагает, что a не является отрицательным, и для любого x^y, a^y
этот ответ отвечает на вопрос неправильно. (Алекс)
посмотрите на поведение A^X mod M
as X
увеличивается. В конце концов он должен перейти в цикл. Предположим, цикл имеет длину P
и начинается после N
действия. Тогда X >= N
подразумевает A^X = A^(X+P) = A^(X%P + (-N)%P + N) (mod M)
. Поэтому мы можем вычислить A^B^C
путем вычисления y=B^C, z = y < N ? y : y%P + (-N)%P + N, return A^z (mod m)
.
обратите внимание, что мы можем рекурсивно применить эту стратегию вверх по дереву мощности, потому что производное уравнение либо имеет показатель M или показатель, включающий меньшую башню с меньшим показателем дивидент.
единственный вопрос, если вы можете эффективно вычислить N
и P
дано A
и M
. Обратите внимание, что переоценка N
- это хорошо. Мы можем просто установить N
до M
и все получится. P
- это немного сложнее. Если A
и M
разные простые числа, тогда P=M-1
. Если A
все M
простые факторы, то мы застряли на 0 и P=1
. Я оставляю это как упражнение, чтобы понять это, потому что я не знаю как.
///Returns equivalent to list.reverse().aggregate(1, acc,item => item^acc) % M
func PowerTowerMod(Link<int> list, int M, int upperB = M)
requires M > 0, upperB >= M
var X = list.Item
if list.Next == null: return X
var P = GetPeriodSomehow(base: X, mod: M)
var e = PowerTowerMod(list.Next, P, M)
if e^X < upperB then return e^X //todo: rewrite e^X < upperB so it doesn't blowup for large x
return ModPow(X, M + (e-M) % P, M)
ответ Tacet хорош, но возможны существенные упрощения.
силы x, mod m, являются предпериодическими. Если x относительно прост до m, то степени x являются периодическими, но даже без этого предположения часть перед периодом не длинна, самое большее максимум показателей в простой факторизации m, которая не более log_2 m. Длина периода делит phi (m), и на самом деле лямбда(m), где лямбда функция Кармайкла, в максимальный мультипликативный порядок mod m. Это может быть значительно меньше, чем phi(m). Лямбда(m) может быть быстро вычислена из простой факторизации m, так же как и phi (m). Лямбда(м) НОД лямбда(p_i^e_i) за все полномочия премьер-p_i^e_i в Премьер-факторизации м, и на нечетное простое полномочия, лямбда(p_i^e_i) = фи(p_i^е^я). лямбда(2)=1, ламнда(4)=2, лямбда(2^n)=2^(n-2) для больших степеней 2.
определите modPos (a, n), чтобы быть представителем класса конгруэнтности a в {0,1,.., n-1}. Для неотрицательного a это всего лишь%n. Для отрицательного, по какой-то причине%n определяется как отрицательный,поэтому modPos(a, n) - (a%n)+n.
определите modMin (a,n,min) как наименьшее положительное целое число, конгруэнтное моду n, которое по крайней мере min. Для положительного результата вы можете вычислить это как min+modPos (a-min,n).
Если b^C^... меньше log_2 m (и мы можем проверить, выполняется ли это неравенство рекурсивно, принимая логарифмы), то мы можем просто вычислить a^b^c^... В противном случае a^b^c^... mod m = a^modMin(b^c^..., лямбда (m), [log_2 m])) mod m = a^modMin(b^c^... mod lambda(m), лямбда (m),[log_2 m]).
например, предположим, что мы хотим вычислить 2^3^4^5 мод 100. Обратите внимание, что 3^4^5 имеет только 489 цифр, поэтому это выполнимо другими методами, но оно достаточно большое, чтобы вы не хотели вычислять его напрямую. Однако, методы, которые я дал здесь, вы можете вычислить 2^3^4^5 мод 100 вручную.
С 3^4^5 > log_2 100,
2^3^4^5 mod 100
= 2^modMin(3^4^5,lambda(100),6) mod 100
= 2^modMin(3^4^5 mod lambda(100), lambda(100),6) mod 100
= 2^modMin(3^4^5 mod 20, 20,6) mod 100.
давайте вычислить 3^4^5 mod 20. Так как 4^5 > log_2 20,
3^4^5 mod 20
= 3^modMin(4^5,lambda(20),4) mod 20
= 3^modMin(4^5 mod lambda(20),lambda(20),4) mod 20
= 3^modMin(4^5 mod 4, 4, 4) mod 20
= 3^modMin(0,4,4) mod 20
= 3^4 mod 20
= 81 mod 20
= 1
мы можем подключить это к предыдущему расчету:
2^3^4^5 mod 100
= 2^modMin(3^4^5 mod 20, 20,6) mod 100
= 2^modMin(1,20,6) mod 100
= 2^21 mod 100
= 2097152 mod 100
= 52.
обратите внимание, что 2^(3^4^5 mod 20) mod 100 = 2^1 mod 100 = 2, что неверно. Вы не можете уменьшить до допериодической части мощности базы.