найти ^ b ^ c ^ ... мод м - PullRequest
27 голосов
/ 19 ноября 2010

Я хотел бы рассчитать:

a b c d . . . mod m

Знаете ли вы какой-нибудь эффективный способ, поскольку это число слишком велико, но a, b, c, ... и m помещаются в простое 32-разрядное целое число.

Есть идеи?


Предупреждение: Этот вопрос отличается от поиска b mod m.

Также обратите внимание, что a b c не совпадает с (a b ) c . Последний равен bc . Экспонирование является правоассоциативным.

Ответы [ 6 ]

20 голосов
/ 19 ноября 2010

a b c mod m = a b c mod n mod m, где n = φ (m) Сложная функция Эйлера .

Если m простое, то n = m-1.

Редактировать: как указал Набб, это верно, только если a взаимно простое с m.Так что вам нужно сначала проверить это.

13 голосов
/ 14 декабря 2014

Ответ не содержит полного формального математического доказательства правильности.Я предположил, что это не нужно здесь.Кроме того, это было бы очень неразборчиво на SO (например, без MathJax).Я буду использовать (чуть чуть) специфический простой факторизация алгоритм.Это не лучший вариант, но достаточно.

tl; dr

Мы хотим вычислить a^x mod m.Мы будем использовать функцию modpow(a,x,m).Описано ниже.

  1. Если x достаточно мало (не имеет экспоненциальной формы или существует p^x | m), просто вычислите его и верните
  2. Разделите на простые числа и вычислите p^x mod m отдельно длякаждое простое число, используя modpow функцию
    1. Расчет c' = gcd(p^x,m) и t' = totient(m/c')
    2. Расчет w = modpow(x.base, x.exponent, t') + t'
    3. Сохранение pow(p, w - log_p c', m) * c' в A Таблица
  3. Множество всех элементов из A и возврат по модулю m

Здесь pow должно выглядеть как паутинка питона.

Основная проблема:

Поскольку текущий лучший ответ касается только особого случая gcd(a,m) = 1, и OP не рассмотрел данное предположение, я решил написать этот ответ.Я также буду использовать полную теорему Эйлера .Цитируя Википедию:

Теорема Эйлера :Если n и a являются взаимно простыми положительными целыми числами, то enter image description here где φ (n) равно функция времени Эйлера .

Предположение numbers are co-prime очень важно, как показывает Nabb в комментарии .Итак, во-первых, мы должны убедиться, что числа взаимно просты.(Для большей ясности предположим, что x = b^(c^...).) Поскольку a^x mod m = ((p1^alpha)^x mod m)*(p2..., где a = p1^alpha * p2^..., мы можем разложить a, отдельно вычислить q1 = (p1^alpha)^x mod m,q2 = (p2^beta)^x mod m... и затем вычислить ответ простым способом (q1 * q2 * q3 * ... mod m).Число имеет не более o(log a) простых факторов, поэтому мы будем вынуждены выполнить не более o(log a) вычислений.

Фактически нам не нужно делить на каждый простой фактор a (если нетвсе встречаются в m с другими показателями), и мы можем комбинировать с одним и тем же показателем, но это пока не заслуживает внимания.

Теперь рассмотрим проблему (p^z)^x mod m, где p простое.Обратите внимание на важное замечание:

Если a,b - положительные целые числа, меньшие m, а c - некоторое положительное целое число и a equiv b mod m, тогда true - это предложение ac equiv bc mod mc.

Используя приведенное выше наблюдение, мы можем получить решение для актуальной проблемы.Мы можем легко рассчитать gcd((p^z)^x, m).Если x * z большие, это число, сколько раз мы можем разделить m на p.Пусть m' = m /gcd((p^z)^x, m).(Уведомление (p^z)^x = p^(z*x).) Пусть c = gcd(p^(zx),m).Теперь мы можем легко (см. Ниже) вычислить w = p^(zx - c) mod m', используя теорему Эйлера, потому что эти числа взаимно просты!А после, используя приведенное выше наблюдение, мы можем получить p^(zx) mod m.Исходя из вышеизложенного предположения wc mod m'c = p^(zx) mod m, поэтому ответ на данный момент - p^(zx) mod m = wc, а w,c легко вычислить.

Поэтому мы можем легко вычислить a^x mod m.

Вычислить a^x mod mиспользуя теорему Эйлера

Теперь предположим, что a,m взаимно просты.Если мы хотим вычислить a^x mod m, мы можем вычислить t = totient(m) и заметить a^x mod m = a^(x mod t) mod m.Это может быть полезно, если x велико и мы знаем только определенное выражение x, например, x = 7^200.

Посмотрите на пример x = b^c.мы можем вычислить t = totient(m) и x' = b^c mod t, используя возведение в степень, возводя в квадрат алгоритм во Θ(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)

Обратите внимание на простые факты:

  1. если gcd(a,b)=1, то φ(ab) = φ(a)*φ(b)
  2. , если p простое φ(p^k)=(p-1)*p^(k-1)

Следовательно, мы можем разложить на множители n (ак. n = p1^k1 * p2^k2 * ...) и отдельно рассчитать φ(p1^k1),φ(p2^k2),..., используя факт 2. Затем скомбинируем это, используя факт 1. φ(n)=φ(p1^k1)*φ(p2^k2)*...

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

пример: (правильно, по той же причине, что и этот алгоритм факторизации )

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))

Дело: abc mod m

Потому что на самом деле он делает одно и то же много раз, я думаю, этот случай покажет вам, как решить эту проблему в целом.
Во-первых, мы должны разделить a на основные силы. Лучшим представлением будет пара <number, exponent>.
пример:

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

Похоже, это работает.
DEMO и это правильно !

1 голос
/ 19 ноября 2010

Модульное экспонирование является правильным способом решения этой проблемы, вот несколько подсказок:

Чтобы найти b c d % m Вы должны начать с вычисления% m, затем a b % m, затем a b c % m и затем a b. c d % m ... (вы поняли)

Чтобы найти b % m, вам в основном нужнодве идеи: [Пусть B = этаж (b / 2)]

a b = (a B ) 2 , если b четноеИЛИ a b = (a B ) 2 * a, если b нечетно. (X * Y)% m = ((X% m) * (Y% m))% m
(% = mod)

Следовательно,
, если bчетное
a b % m = (a B % m) 2 % m
или если bнечетное
a b % m = (((a B % m) 2 ) * (a% m))% m

Таким образом, если вы знали значение B , вы можете рассчитать это значение.

Чтобы найти B , примените аналогичный подход, деление B, пока вы не достигнете 1.

например, для расчета 16 13 % 11:

16 13 % 11 = (16% 11) 13 % 11 = 5 13 % 11 = (5 6 % 11) * (5 6 % 11) *(5% 11) <---- (I) <br>
Найти 5 6 % 11:
5 6 % 11 = ((5 3 % 11) * (5 3 % 11))% 11 <---- (II) <br>Найти 5 3 % 11:
5 3 % 11 = ((5 1 % 11) * (5 1 % 11) * (5% 11))% 11
= ((((5 * 5)% 11) * 5)% 11 = ((25% 11) * 5)% 11 = (3* 5)% 11 = 15% 11 = 4
Включение этого значения в (II) дает
5 6 % 11 = (((4 * 4)% 11) * 5)%11 = ((16% 11) * 5)% 11 = (5 * 5)% 11 = 25% 11 = 3
Включение этого значения в (I) дает
5 13 %11 = ((3% 11) * (3% 11) * 5)% 11 = ((9% 11) * 5)% 11 = 45% 11 = 4

Таким образом 5 13 % 11 = 4
С этим вы можете вычислить что-нибудь в форме a 5 13 % 11 и так далее ...

1 голос
/ 19 ноября 2010
  1. Поскольку для любого отношения a=x^y отношение является инвариантным относительно используемой вами числовой базы (база 2, база 6, база 16 и т. Д.).

  2. Поскольку операция mod N эквивалентна извлечению младшей значащей цифры (LSD) в базе N

  3. Поскольку на LSD результата A в базе 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


Этот ответ отвечает на неправильный вопрос.(Alex)

0 голосов
/ 13 июня 2015

Ответ Tacet хорош, но возможны существенные упрощения.

Степени x, mod m, являются периодическими. Если x относительно простое число по отношению к m, степени x являются периодическими, но даже без этого допущения, часть перед периодом не длинная, максимум максимум показателей степени в простой факторизации m, который составляет самое большее log_2 m. , Длина периода делит фи (м) и фактически лямбда (м), где лямбда есть функция Кармайкла , максимальный мультипликативный порядок mod m. Это может быть значительно меньше, чем фи (м). Лямбда (m) может быть быстро вычислена из простой факторизации m, так же как и фи (m). Лямбда (m) - это GCD лямбды (p_i ^ e_i) над всеми простыми степенями p_i ^ e_i в простой факторизации m, а для нечетных простых степеней лямбда (p_i ^ e_i) = phi (p_i ^ e ^ i). лямбда (2) = 1, лямда (4) = 2, лямбда (2 ^ n) = 2 ^ (n-2) для больших степеней 2.

Определите modPos (a, n), который будет представлять класс конгруэнции a в {0,1, .., n-1}. Для неотрицательных а это просто% n. Для отрицательного по какой-то причине% n определяется как отрицательный, поэтому modPos (a, n) равно (a% n) + n.

Определите modMin (a, n, min) как наименее положительное целочисленное конгруэнтное значение для mod n, которое составляет не менее min. Для положительного результата вы можете вычислить это как min + modPos (a-min, n).

Если b ^ c ^ ... меньше чем log_2 m (и мы можем проверить, выполняется ли это неравенство, рекурсивно принимая логарифмы), то мы можем просто вычислить a ^ b ^ c ^ ... В противном случае a ^ b ^ c ^ ... mod m = a ^ modMin (b ^ c ^ ..., lambda (m), [log_2 m])) mod m = a ^ modMin (b ^ c ^ ... mod lambda (m ), лямбда (м), [log_2 м]).

Например, предположим, что мы хотим вычислить 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, что неверно. Вы не можете уменьшить до предериодической части полномочий базы.

0 голосов
/ 19 ноября 2010

Посмотрите на поведение A^X mod M при увеличении 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).

Обратите внимание, что мы можем рекурсивно применять эту стратегию вверх по дереву степеней, потому что производное уравнение имеет либо показатель степени <<code>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)
...