поиск 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). Описанный под.

  1. если x достаточно мал (не экспоненциальная форма или существует p^x | m) просто вычислить его и вернуть
  2. разделить на простые числа и вычислить p^x mod m отдельно для каждого штриха, используя Эйлера теорема. Цитата из Википедии:

    Эйлера теорема:
    Если n и a являются coprime положительные целые числа, тогда 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) 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)

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

    1. если gcd(a,b)=1, потом φ(ab) = φ(a)*φ(b)
    2. если 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))
    

    корпус: abc 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)]

  • ab = (aB)2 если b четное или ab = (aB)2 * a, если b нечетно.
  • (X*Y)%m = ((X%m) * (Y%m)) % m
    (% = ультрасовременный)

    таким образом,
    если 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 и так далее...


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

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

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