**模幂**（英語：）是一种对模进行的冪运算，在计算机科学，尤其是公开密钥加密方面有一定用途。

模幂运算是指求整数b的e次方{\displaystyle b^{e}}被正整数m所除得到的余数c的过程，可用数学符号表示为{\displaystyle c=b^{e}{\bmod {m}}}。由c的定义可得{\displaystyle 0\leq c<m}。

例如，给定{\displaystyle b=5}，{\displaystyle e=3}和{\displaystyle m=13}，{\displaystyle 5^{3}=125}被13除得的余数{\displaystyle c=8}。

指数e为负数时可使用扩展欧几里得算法找到b模除m的模逆元d来执行模幂运算，即：

{\displaystyle c=b^{e}{\bmod {m}}=d^{-e}{\bmod {m}}}，其中 {\displaystyle e<0}且{\displaystyle b\cdot d\equiv 1{\pmod {m}}}。
即使在整数很大的情况下，上述模幂运算其实也是易于执行的。然而，计算模的离散对数（即在已知b，c和m时求出指数e）则较为困难。这种类似單向函數的表现使模幂运算可用于加密算法。

直接算法
----

计算模幂的最直接方法是直接算出{\displaystyle b^{e}}，再把结果模除m。假设已知{\displaystyle b=4}，{\displaystyle e=13}，以及{\displaystyle m=497}，要求c：

{\displaystyle c\equiv 4^{13}{\pmod {497}}}
可用计算器算得413结果为67,108,864，模除497，可得*c*等于445。

注意到b只有一位，e也只有两位，但{\displaystyle b^{e}}的值却有8位。

强加密时b通常至少有1024位。考虑{\displaystyle b=5\times 10^{76}}和{\displaystyle e=17}的情况，b的长度为77位，e的长度为2位，但是{\displaystyle b^{e}}的值以十进制表示却已经有1304位。现代计算机虽然可以进行这种计算，但计算速度却大大降低。

用这种算法求模幂所需的时间取决于操作环境和处理器，时间复杂度为{\displaystyle \mathrm {O} (e)}。

内存优化
----

这种方法比第一种所需要的步骤更多，但所需内存和时间均大为减少，其原理为：
给定两个整数a和b，以下两个等式是等价的：

{\displaystyle c{\bmod {m}}=(a\cdot b){\bmod {m}}}
{\displaystyle c{\bmod {m}}=[(a{\bmod {m}})\cdot (b{\bmod {m}})]{\bmod {m}}}
算法如下：

1. 令c=1，{\displaystyle e'=0}。
2. {\displaystyle e'}自增1。
3. 令{\displaystyle c=(b\cdot c){\bmod {m}}}.
4. 若{\displaystyle e'<e}，则返回第二步；否则，c即为{\displaystyle c\equiv b^{e}{\pmod {m}}}。

再以{\displaystyle b=4}，{\displaystyle e=13}，{\displaystyle m=497}为例说明，算法第三步需要执行13次：

* {\displaystyle e'=1\cdot c=(1\cdot 4){\bmod {4}}97=4{\bmod {4}}97=4}
* {\displaystyle e'=2\cdot c=(4\cdot 4){\bmod {4}}97=16{\bmod {4}}97=16}
* {\displaystyle e'=3\cdot c=(16\cdot 4){\bmod {4}}97=64{\bmod {4}}97=64}
* {\displaystyle e'=4\cdot c=(64\cdot 4){\bmod {4}}97=256{\bmod {4}}97=256}
* {\displaystyle e'=5\cdot c=(256\cdot 4){\bmod {4}}97=1024{\bmod {4}}97=30}
* {\displaystyle e'=6\cdot c=(30\cdot 4){\bmod {4}}97=120{\bmod {4}}97=120}
* {\displaystyle e'=7\cdot c=(120\cdot 4){\bmod {4}}97=480{\bmod {4}}97=480}
* {\displaystyle e'=8\cdot c=(480\cdot 4){\bmod {4}}97=1920{\bmod {4}}97=429}
* {\displaystyle e'=9\cdot c=(429\cdot 4){\bmod {4}}97=1716{\bmod {4}}97=225}
* {\displaystyle e'=10\cdot c=(225\cdot 4){\bmod {4}}97=900{\bmod {4}}97=403}
* {\displaystyle e'=11\cdot c=(403\cdot 4){\bmod {4}}97=1612{\bmod {4}}97=121}
* {\displaystyle e'=12\cdot c=(121\cdot 4){\bmod {4}}97=484{\bmod {4}}97=484}
* {\displaystyle e'=13\cdot c=(484\cdot 4){\bmod {4}}97=1936{\bmod {4}}97=445}

因此最终结果c为445，与第一种方法所求结果相等。

与第一种方法相同，这种算法需要{\displaystyle \mathrm {O} (e)}的时间才能完成。但是，由于在计算过程中处理的数字比第一种算法小得多，因此计算时间至少减少了{\displaystyle \mathrm {O} (e)}倍。

算法伪代码如下：

```
**function** modular_pow(b, e, m)
    **if** m = 1
        **then** **return** 0
    c := 1
    **for** e' = 0 **to** e-1
        c := (c * b) **mod** m
    **return** c
```

从右到左二位算法
--------

第三种方法结合了第二种算法和平方求幂原理，使所需步骤大大减少，同时也与第二种方法一样减少了内存占用量。

首先把e表示成二进制，即：

{\displaystyle e=\sum \_{i=0}^{n-1}a\_{i}2^{i}}
此时e的长度为n位。对任意i（{\displaystyle 0\leq i<n}），a\_{i}可取0或1任一值。由定义有{\displaystyle a\_{n-1}=1}。

{\displaystyle b^{e}}的值可写作：

{\displaystyle b^{e}=b^{\left(\sum \_{i=0}^{n-1}a\_{i}2^{i}\right)}=\prod \_{i=0}^{n-1}\left(b^{2^{i}}\right)^{a\_{i}}}
因此答案c即为：

{\displaystyle c\equiv \prod \_{i=0}^{n-1}\left(b^{2^{i}}\right)^{a\_{i}}\ ({\mbox{mod}}\ m)}
### 伪代码

下述伪代码基于布魯斯·施奈爾所著《应用密码学》。其中base，exponent和modulus分别对应上式中的b，e和m。

```
**function** modular_pow(base, exponent, modulus)
    **if** modulus = 1 **then** **return** 0
    Assert :: (modulus - 1) * (modulus - 1) does not overflow base
    result := 1
    base := base **mod** modulus
    **while** exponent > 0
        **if** (exponent **mod** 2 == 1):
           result := (result * base) **mod** modulus
        exponent := exponent >> 1
        base := (base * base) **mod** modulus
    **return** result
```

注意到在首次进入循环时，变量base等于b。在第三行代码中重复执行平方运算，会确保在每次循环结束时，变量base等于{\displaystyle b^{2^{i}}{\bmod {m}}}，其中i是循环执行次数。

本例中，底数b的指数为{\displaystyle e=13}。
指数用二进制表示为1101，有4位，故循环执行4次。
4位数字从右到左依次为1，0，1，1。

首先，初始化结果R为1，并将*b*的值保存在变量x中：

{\displaystyle R\leftarrow 1\,(=b^{0}){\text{且 }}x\leftarrow b}.
第1步 第1位为1，故令{\displaystyle R\leftarrow R\cdot x{\text{ }}(=b^{1})}；
令{\displaystyle x\leftarrow x^{2}{\text{ }}(=b^{2})}。
第2步 第2位为0，故不给*R*赋值；
令{\displaystyle x\leftarrow x^{2}{\text{ }}(=b^{4})}。
第3步 第3位为1，故令{\displaystyle R\leftarrow R\cdot x{\text{ }}(=b^{5})}；
令{\displaystyle x\leftarrow x^{2}{\text{ }}(=b^{8})}。
第4步 第4位为1，故令{\displaystyle R\leftarrow R\cdot x{\text{ }}(=b^{13})}；
这是最后一步，所以不需要对*x*求平方。
综上，R为{\displaystyle b^{13}}。

以下计算{\displaystyle b=4}的{\displaystyle e=13}次方对497求模的结果。

初始化：

{\displaystyle R\leftarrow 1\,(=b^{0})}且{\displaystyle x\leftarrow b=4}。
第1步 第1位为1，故令{\displaystyle R\leftarrow R\cdot 4{\pmod {497}}\equiv 4{\pmod {497}}}；
令{\displaystyle x\leftarrow x^{2}{\text{ }}(=b^{2})\equiv 4^{2}\equiv 16{\pmod {497}}}。
第2步 第2位为0，故不给*R*赋值；
令{\displaystyle x\leftarrow x^{2}{\text{ }}(=b^{4})\equiv 16^{2}{\pmod {497}}\equiv 256{\pmod {497}}}。
第3步 第3位为1，故令{\displaystyle R\leftarrow R\cdot x{\text{ }}(=b^{5})\equiv 4\cdot 256{\pmod {497}}\equiv 30{\pmod {497}}}；
令{\displaystyle x\leftarrow x^{2}{\text{ }}(=b^{8})\equiv 256^{2}{\pmod {497}}\equiv 429{\pmod {497}}}。
第4步 第4位为1，故令{\displaystyle R\leftarrow R\cdot x{\text{ }}(=b^{13})\equiv 30\cdot 429{\pmod {497}}\equiv 445{\pmod {497}}}；
综上，R为{\displaystyle 4^{13}{\pmod {497}}\equiv 445{\pmod {497}}}，与先前算法中所得结果相同。

该算法时间复杂度为O(log exponent)。指数exponent值较大时，这种算法与前两种O(exponent)算法相比具有明显的速度优势。例如，如果指数为220 = 1048576，此算法只需执行20步，而非1,048,576步。

### Lua实现

```
**function** modPow(b,e,m)
        **if** m == 1 **then**
                **return** 0
        **else**
                **local** r = 1
                b = b % m
                **while** e > 0 **do**
                        **if** e % 2 == 1 **then**
                                r = (r*b) % m
                        **end**
                        e = e >> 1     --Lua 5.2或更早版本使用e = math.floor(e / 2)
                        b = (b^2) % m
                **end**
                **return** r
        **end**
**end**
```

软件实现
----

鉴于模幂运算是计算机科学中的重要操作，并且已有高效算法，所以许多编程语言和高精度整数库都有执行模幂运算的函数：

* Python内置的`pow()`（求幂）函数 （页面存档备份，存于）
* .NET框架的`BigInteger`类的`ModPow()`方法
* Java的`java.math.BigInteger`类的`modPow()`方法
* Perl的`Math::BigInt`模块的`bmodpow()`方法 （页面存档备份，存于）
* Raku内置的`expmod`例程
* Go的`big.Int`类的`Exp()`（求幂）方法 （页面存档备份，存于）
* PHP的BC Math库的`bcpowmod()`函数 （页面存档备份，存于）
* GNU多重精度运算库（GMP）的`mpz_powm()`函数 （页面存档备份，存于）
* FileMaker Pro的`@PowerMod()`函数（以1024位RSA加密为例）
* Ruby的`openssl`包的`OpenSSL::BN#mod_exp`方法 （页面存档备份，存于）
* HP Prime计算器的CAS.powmod()函数[]

另见
--

* 蒙哥马利算法，用于计算模很大时的余数。

参考资料
----

1. ↑ . weakdh.org.  [2019-05-03]. （原始内容存档于2021-03-29）.
2. ↑  Schneier 1996, p. 244.

外部链接
----

* Schneier, Bruce.  2nd. Wiley. 1996. ISBN 978-0-471-11709-4.
* Paul Garrett, Fast Modular Exponentiation Java Applet （页面存档备份，存于）
* Gordon, Daniel M.  (PDF). Journal of Algorithms (Elsevier BV). 1998, **27** (1): 129–146  [2019-11-30]. ISSN 0196-6774. doi:10.1006/jagm.1997.0913. （原始内容存档 (PDF)于2019-08-27）.

|  |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  |  |
| --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- | --- |
| 

| * 
* 论
* 编
数论算法 |
| --- |
|  |
| 素数测试 | 
* AKS
* APR（英语：）
* Baillie–PSW（英语：）
* 椭圆曲线（英语：）
* Pocklington（英语：）
* 费马
* 卢卡斯（英语：）
* *卢卡斯-莱默*
* *Lucas–Lehmer–Riesel（英语：）*
* *普罗斯*
* *Pépin（英语：）*
* 二次Frobenius（英语：）
* Solovay–Strassen（英语：）
* 米勒-拉宾

 |
|  |
| 質數生成（英语：） | 
* 阿特金筛法（英语：）
* 埃拉托斯特尼筛法
* Pritchard筛法（英语：）
* Sundaram筛法（英语：）
* 輪式因數分解法（英语：）

 |
|  |
| 整数分解 | 
* 連分數分解 (CFRAC)（英语：）
* Dixon's（英语：）
* Lenstra橢圓曲線 (ECM)（英语：）
* 欧拉因式分解法（英语：）
* 波拉德ρ算法（英语：）
* *p* − 1（英语：）
* *p* + 1（英语：）
* 二次篩選法
* 普通数域筛选法
* *特殊數域篩選法 (SNFS)（英语：）*
* 有理筛选法
* 费马因式分解法（英语：）
* Shanks二次形式（英语：）
* 试除法
* 秀爾演算法

 |
|  |
| 乘法算法 | 
* 古埃及乘算（英语：）
* 長乘法
* 卡拉楚巴算法
* 图姆-库克算法
* 頌哈吉-施特拉森演算法
* 富尔算法（英语：）

 |
|  |
| 歐幾里德除法除法算法 | 
* 二進制（英语：）
* 倍塊法（英语：）
* Fourier（英语：）
* Goldschmidt（英语：）
* Newton-Raphson（英语：）
* 長除法
* 短除法
* SRT

 |
|  |
| 离散对数 | 
* 大步小步算法
* 波拉德ρ算法
* Pollard kangaroo（英语：）
* Pohlig–Hellman（英语：）
* Index calculus（英语：）
* Function field sieve（英语：）

 |
|  |
| 最大公因數 | 
* 二进制最大公因数算法（英语：）
* 輾轉相除法
* 扩展欧几里得算法
* Lehmer's（英语：）

 |
|  |
| 二次剩余 | 
* Cipolla（英语：）
* Pocklington's（英语：）
* Tonelli–Shanks（英语：）
* Berlekamp（英语：）
* Kunerth（英语：）

 |
|  |
| 其他算法 | 
* Chakravala（英语：）
* Cornacchia（英语：）
* 整數關係（英语：）（LLL（英语：）；KZ（英语：））
* 平方求冪
* 整数平方根
* 模幂运算
* 蒙哥马利算法
* Schoof（英语：）
* 特拉亨伯格系統（英语：）

 |
|  |
| 
* *斜体*表示该算法只适用于特殊形式的数字

 |

 |