26


14

a ^ b ^ c ^を見つける…​ mod m

計算したい:

a ^ b ^ c ^ d ^^^ mod m

この数は大きすぎますが、a、b、cなど、効率的な方法を知っていますか…​ mは単純な32ビット整数に適合します。

何か案は?

'' '' '

*注意:*この質問はa ^ b ^ mod mを見つけることとは異なります。

また、a ^ b ^ c ^^は(a ^ b c ^と同じではないことに注意してください。 後者はa ^ bc ^と同じです。 べき乗は右結合です。

6 Answer


20


a ^ b ^ c ^^ mod m = a ^ b ^ c ^ mod n ^ mod m、n =φ(m)http://en.wikipedia.org/wiki/Euler’s_totient_function[Euler’s totient function]。

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

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


13


_ 答えには、正確さの完全な正式な数学的証明が含まれていません。 ここでは不要だと思いました。 それに、SOでは非常に判読できません(たとえば、MathJaxはありません)。 +(ほんの少し)特定のhttps://math.stackexchange.com/a/1037236/186012[prime factorization]アルゴリズムを使用します。 最適なオプションではありませんが、十分です。 _

tl; dr

`a ^ x mod m`を計算します。 関数 `modpow(a、x、m)`を使用します。 以下で説明します。

  1. 「x」が十分に小さい場合(指数形式ではないか、「p ^ x | m」が存在する場合) それを計算して返す

  2. 素数に分割し、各素数に対して個別に `p ^ x mod m`を計算します。 「modpow」関数を使用する

  3. `c '= gcd(p ^ x、m)`および `t' = totient(m / c ')`を計算します

  4. `w = modpow(x.base、x.exponent、t ')+ t'`を計算します

  5. A`テーブルに pow(p、w-log_p c '、m)* c'`を保存します

  6. Aのすべての要素を多重化し、モジュロmを返します

ここで、「pow」はpythonのパウのように見えるはずです。

主な問題:

現在のベストアンサーは、特別なケース `gcd(a、m)= 1`のみに関するものであり、OPはこの仮定を考慮していないため、このアンサーを書くことにしました。 また、https://en.wikipedia.org/wiki/Euler%27s_theorem [Euler’s totient theorem]も使用します。 ウィキペディアの引用:

_ オイラーのトーテット定理:+「n」と「a」が互いに正の整数である場合、image:https://i.stack.imgur.com/1jWa0.png [ここに画像の説明を入力]φ(n)はhttps://en.wikipedia.org/wiki/Euler%27s_totient_function[Euler’s totient function]。 _

Nabb https://stackoverflow.com/questions/4223313/finding-abc-mod-m#comment4570282_4223480 [コメントで表示]のように、「数字は互いに素」という仮定は非常に重要です。 したがって、まず、数値が互いに素であることを確認する必要があります。 (より明確にするため、 `x = b ^(c ^ …​)`と仮定します。)image:https://i.stack.imgur.com/ok403.png [a ^ x mod m =((p1 ^ alpha )^ x mod m)*(p2 …​]、ここで、画像:https://i.stack.imgur.com/kb1Nx.png [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」が正の整数と画像である場合:https://i.stack.imgur.com/gThRv.png [a equiv b mod m]、それからtrue文イメージ:https://i.stack.imgur.com/NXIrh.png [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 = 7 ^ 200`のように `x`の特定の表現しかわからない場合に役立ちます。

例 `x = b ^ c`を見てください。 「Θ(log c)」のhttps://en.wikipedia.org/wiki/Exponentiation_by_squaring[exponentiation by squareing]アルゴリズムを使用して、 `t = totient(m)`および `x '= b ^ c mod t`を計算できます時間。 そして(同じアルゴリズムを使用して) `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) …​

繰り返しtotientを計算する場合、Sieve of Eratosthenesを使用して素数をテーブルに保存することを忘れないでください。 定数が減少します。

  • link:/ questions / tagged / python [python]例:*(https://math.stackexchange.com/a/1037236/186012[this factorization algorithm]と同じ理由で正しい)

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」を素数の力に分割する必要があります。 最適な表現はペア ''です。 + * link:/ questions / tagged / c%2b%2b11 [c + 11]の例:*

std::vector> split(unsigned n) {
  std::vector> 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) 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

動作するように見えます。 + * http://ideone.com/bOxFqJ [DEMO] *およびhttp://www.wolframalpha.com/input/?i=%28109315800409857451726136757650%5E%289878789%5E1214712%29%29%20mod%201948502738 [正しい] ]!


1


  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

and

(X ^ Y) mod N = ((X mod N) ^ Y) mod N)

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

'' '' '

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

'' '' '

この答えは間違った質問に答えます。 (alex)


1


モジュラーべき乗法はこの問題を解決する正しい方法です。ここにちょっとしたヒントがあります:+
a ^ b ^ c ^ d ^%mを見つけるには、%m、a ^ b ^%m、a ^ b ^ c ^^%m、a ^ b ^ c ^の計算から始めなければなりません。 d ^%m …​ (あなたはアイデアを得る)

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

bが偶数の場合a ^ b ^ =(a ^ B 2 ^またはbが奇数の場合a ^ b ^ =(a ^ B 2 ^ * a

(X * Y)%m =((X%m)*(Y%m))%m +(%= mod)

したがって、+ if bが偶数 + a ^ b ^%m =(a ^ B %m) 2 ^%m + or b​​が奇数の場合 + a ^ b ^%m =(((a ^ B ^% m)^ 2 ^)*(a%m))%m

したがって、a ^ B ^の値を知っていれば、この値を計算できます。

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

e.g. 16 ^ 13 ^%11を計算するには:+

16 ^ 13 ^%11 =(16%11)^ 13 ^%11 = 5 ^ 13 ^%11 =(5 ^ 6 ^%11)(5 ^ 6 ^%11)(5%11)← ---(I)+
5 ^ 6 ^%11を見つけるには:+ 5 ^ 6 ^%11 =((5 ^ 3 ^%11)(5 ^ 3 ^%11))%11 ←---(II)+ 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などの形式の任意のものを計算できます…​


0


「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を計算することにより、 A ^ B ^ C`を計算できますか? y:y%P +(-N)%P + N、A ^ z(mod m) `を返します。

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

唯一の問題は、「A」と「M」が与えられたときに「N」と「P」を効率的に計算できるかどうかです。 「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 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


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

xのべき乗、mod mは前周期的です。 xがmに対して比較的素数の場合、xのべき乗は周期的ですが、その仮定がなくても、周期の前の部分は長くはなく、最大でmの素因数分解の指数の最大値、最大でlog_2 mです。 周期の長さはphi(m)を分割し、実際はlambda(m)であり、ここでlambdaはhttp://en.wikipedia.org/wiki/Carmichael_function[Carmichael’s function]、最大乗法mod mです。 これは、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の場合、これはa%nです。 負の場合、何らかの理由でa%nが負であると定義されているため、modPos(a、n)は(a%n)+ nです。

modMin(a、n、min)を、少なくともminであるmod nに一致する最小の正の整数になるように定義します。 正の場合、これを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)、lambda(m)、[log_2 m])。

たとえば、2 ^ 3 ^ 4 ^ 5 mod 100を計算するとします。 3 ^ 4 ^ 5には489桁しかないため、これは他の方法で実行できますが、直接計算したくないほど十分に大きいことに注意してください。 ただし、ここで説明した方法を使用すると、2 ^ 3 ^ 4 ^ 5 mod 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であることに注意してください。これは正しくありません。 ベースのパワーの前周期部分まで減らすことはできません。