web-dev-qa-db-ja.com

a ^ b ^ c ^ ... modmを見つける

計算したい:

abcd mod m

この数は大きすぎますが、a、b、c、...、およびmは単純な32ビット整数に収まるので、効率的な方法を知っていますか。

何か案は?


警告:この質問は、b modm。

また、bc (aと同じではありませんbc。後者はに等しい紀元前。べき乗は右結合です。

28
user182513

答えには、正当性の完全な正式な数学的証明は含まれていません。ここでは不要だと思いました。その上、SOでは非常に判読できません(たとえば、MathJaxはありません)。
(少しだけ)特定の 素因数分解 アルゴリズムを使用します。これは最善の選択肢ではありませんが、十分です。

tl; dr

_a^x mod m_を計算したい。関数modpow(a,x,m)を使用します。以下で説明します。

  1. xが十分に小さい場合(指数形式ではないか、_p^x | m_が存在する場合)、それを計算して
  2. 素数に分割し、modpow function を使用して、素数ごとに個別に_p^x mod m_を計算します。
    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はPythonの捕虜のように見えるはずです。

主な問題:

現在のベストアンサーは特殊なケースgcd(a,m) = 1のみであり、OPはこの仮定を問題として考慮していなかったため、このアンサーを作成することにしました。 オイラーのトーティエント定理 も使用します。ウィキペディアの引用:

オイラーのトーティエント定理
naが互いに素な正の整数の場合、 enter image description here ここで、φ(n)は オイラーのトーティエント関数 です。

Nabb コメントに表示 のように、仮定_numbers are co-prime_は非常に重要です。したがって、最初に、数値が互いに素であることを確認する必要があります。 (わかりやすくするために、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、そして真は文です ac equiv bc mod mc

上記の観察を使用して、実際の問題の解決策を受け取ることができます。 gcd((p^z)^x, m)は簡単に計算できます。 x * zが大きい場合、それはmpで割ることができる回数です。 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 = 7^200_のように、xの特定の式しかわからない場合に役立ちます。

例_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(ak。_n = p1^k1 * p2^k2 * ..._)を因数分解し、ファクト2を使用してφ(p1^k1),φ(p2^k2),...を個別に計算し、ファクト1を使用してこれを組み合わせることができます。φ(n)=φ(p1^k1)*φ(p2^k2)*...

トーティエントを繰り返し計算する場合は、エラトステネスのふるいを使用して素数をテーブルに保存することをお勧めします。それは定数を減らします。

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を素数冪に分割する必要があります。最適な表現はペア_<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が機能しているexample

_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)ak。各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 [〜#〜]および 正しい =!

13
Tacet

abc mod m = abc mod n mod m、ここでn =φ(m) オイラーのトーティエント関数

Mが素数の場合、n = m-1です。

編集:ナブが指摘したように、これはaがmと互いに素である場合にのみ当てはまります。したがって、最初にこれを確認する必要があります。

20
Henrik

べき乗剰余は、この問題を解決する正しい方法です。ここに少しヒントがあります。

を見つけるにはbcd %m最初に%mを計算し、次にab %m、次にabc %m、次にabcd %m ...(あなたはアイデアを得る)

を見つけるにはb %m、基本的に2つのアイデアが必要です:[Let B = floor(b/2)]

  • ab =(aB2 bが偶数の場合OR ab =(aB2* bが奇数の場合はa。
    • (X * Y)%m =((X%m)*(Y%m))%m
      (%= mod)

      したがって、
      bが偶数の場合
      ab %m =(aB %m)2 %m
      またはbが奇数の場合
      ab %m =(((aB %m)2)*(a%m))%m

      だからあなたがの価値を知っていればB、この値を計算できます。

      を見つけるにはB、同様のアプローチを適用し、1に達するまでBを分割します。

      例えば16を計算するには13 %11:

      1613 %11 =(16%11)13 %11 = 513 %11 =(56 %11)*(56 %11)*(5%11)<----(I)

      5を見つけるには6 %11:
      56 %11 =((53 %11)*(53 %11))%11 <----(II)
      5を見つけるには3%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
      これを使用すると、任意の形式の計算を行うことができます513 %11など...

  • 1
    lazyboy
    1. 関係_a=x^y_の場合、関係は使用している基数(基数2、基数6、基数16など)に関して不変であるためです。

    2. Mod N演算は、基数Nの最下位桁(LSD)を抽出することと同等であるため

    3. 基数Nの結果AのLSDは、基数NのXのLSDによってのみ影響を受ける可能性があり、上位の数字は影響を受けないためです。 (例: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)
    _

    したがって、各パワーステップの前にmodを実行できます。これにより、結果が整数の範囲に保たれます。


    これは、aが負ではなく、任意のx ^ yについて、a ^ y <MAXINTであると想定しています。


    この答えは間違った質問に答えます。 (アレックス)

    1
    Alex Brown

    Xが増加するときの_A^X mod M_の動作を見てください。最終的にはサイクルに入る必要があります。サイクルの長さがPで、Nステップの後に開始するとします。次に、_X >= N_はA^X = A^(X+P) = A^(X%P + (-N)%P + N) (mod M)を意味します。したがって、y=B^C, z = y < N ? y : y%P + (-N)%P + N, return A^z (mod m)を計算することにより、_A^B^C_を計算できます。

    導出された方程式には指数<Mがあるか、より小さな指数タワーとより小さな被除数を含む指数があるため、この戦略をパワーツリーに再帰的に適用できることに注意してください。

    唯一の問題は、NPが与えられたときにAMを効率的に計算できるかどうかです。 Nを過大評価しても問題ないことに注意してください。 NMに設定するだけで、問題なく動作します。 Pは少し難しいです。 AMが異なる素数である場合、_P=M-1_。 AMの素因数がすべて含まれている場合、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)
    _
    0
    Craig Gidney

    Tacetの答えは良いですが、かなりの単純化が可能です。

    Xの累乗modmは、前周期的です。 xがmに対して互いに素である場合、xの累乗は周期的ですが、その仮定がなくても、周期の前の部分は長くなく、最大でmの素因数分解の指数の最大値(最大でlog_2 m)です。 。周期の長さはphi(m)を分割し、実際にはlambda(m)を分割します。ここで、lambdaは カーマイケル関数 、最大乗法次数modmです。これは、phi(m)よりも大幅に小さくすることができます。 Lambda(m)は、phi(m)と同様に、mの素因数分解からすばやく計算できます。 Lambda(m)は、mの素因数分解におけるすべての素数冪p_i ^ e_iに対するlambda(p_i ^ e_i)のGCDであり、奇数の素数冪の場合、lambda(p_i ^ e_i)= phi(p_i ^ e ^ i)です。 lambda(2)= 1、lamnda(4)= 2、lambda(2 ^ n)= 2 ^(n-2)の場合、2の累乗が大きくなります。

    ModPos(a、n)を、{0,1、..、n-1}のaの合同クラスを表すように定義します。非負のaの場合、これは単なる%nです。負の場合、何らかの理由でa%nが負であると定義されているため、modPos(a、n)は(a%n)+ nです。

    ModMin(a、n、min)を、少なくともminであるmodnと合同な最小の正の整数になるように定義します。正の場合、これをmin + modPos(a-min、n)として計算できます。

    B ^ c ^ ...がlog_2mよりも小さい場合(そして、この不等式が対数を取ることで成り立つかどうかを確認できます)、単純に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)、lambda (m)、[log_2 m])。

    たとえば、2 ^ 3 ^ 4 ^ 5 mod100を計算するとします。3^ 4 ^ 5は489桁しかないため、これは他の方法でも実行できますが、計算したくないほど大きいことに注意してください。それを直接。ただし、ここで示した方法では、2 ^ 3 ^ 4 ^ 5 mod100を手動で計算できます。

    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 mod20を計算してみましょう。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
    Douglas Zare