计算幂次k次的幂次%m

什么是幂次?

幂次指的是数学中幂运算的次数,例如2的3次幂即为2^3=8,其中3就是幂次。在计算机科学中,幂次也经常被使用到,在信息安全领域中,很多加密算法中都需要使用到幂次及其相关的运算。

什么是模运算?

模运算是指在整数a和正整数m的前提下,找到一个非负的小于m的余数b,使得a=b(mod m)。在计算机科学中,模运算也是十分常见的,在密码学和计算机图形学领域尤其经常使用到模运算。

平方-乘方算法原理

平方算法

平方算法指的是将一个数进行平方运算,通过任意给定的初始值和幂次,计算出指定幂次的平方。它通常使用的是二分法的思想,即将指数不断折半并递归计算,最后将结果进行平方。下面是使用C++实现的平方算法:

long long Square(long long x, long long m) {

if (m == 0) return 1 % mod;

long long res = Square(x, m / 2);

return m % 2 ? res * res % mod * x % mod : res * res % mod;

}

上述代码中,x为初始值,m为幂次,Square为递归计算的函数。如果幂次m为0,则返回1;否则递归计算,最后返回结果。其中,%mod表示对mod取余,是一种常用的保证结果不会过大的方法。

乘方算法

乘方算法指的是将一个数进行指数运算,通过任意给定的初始值、幂次和模数$m$,计算出指定幂次的指数。它通常使用的是二分法的思想,即将指数不断折半并递归计算,最后将结果进行模运算得到最终结果。下面是使用C++实现的乘方算法:

long long QuickPow(long long x, long long m, long long mod) {

if (m == 0) return 1 % mod;

long long res = QuickPow(x, m / 2, mod);

return m % 2 ? res * res % mod * x % mod : res * res % mod;

}

上述代码中,x为初始值,m为幂次,mod为模数,QuickPow为递归计算的函数。如果幂次m为0,则返回1;否则递归计算,最后返回结果。同样地,%mod表示对mod取余,是一种常用的保证结果不会过大的方法。

使用平方-乘方算法计算幂次k次的幂次%m

平方-乘方算法可以很方便地计算出幂次及其余数,那么如何使用它来达到计算幂次k次的幂次模m的目的呢?

假设我们要计算x的y次方再对z取模,即$x^y\%z$。可以先将y用二进制表示,例如,假设y的二进制位为10110,即32+16+4=52,那么有$x^{52}=x^{32} \times x^{16} \times x^{4}$。由此可以得出计算x的y次方的代码:

long long qpow(long long x, long long y, long long mod) {

long long res = 1;

while (y) {

if (y & 1) res = res * x % mod;

x = x * x % mod;

y >>= 1;

}

return res;

}

上述代码中,x为初始值,y为幂次,mod为模数,qpow为计算幂次的函数。y通过二进制表示,然后根据位数依次计算$x^{2^i}$,最后将结果相乘即可。其中,&表示按位与操作,>>表示右移操作。

可以将上述代码修改,使用平方-乘方算法,计算幂次k次后对m取模,即$x^{x^k\% \phi(m)+\phi(m)}\%\ m$。修改后的代码如下:

long long Solve(long long x, long long k, long long m) {

long long phi = Euler(m), t = k % phi + phi;

if (x == 1 || m == 1) return 0;

return qpow(x, Square(x, t - 1, m), m);

}

上述代码中,Solve为计算幂次的函数,phi为欧拉函数,Euler为计算欧拉函数的函数。如果x为1或m为1,则返回0;否则先计算出 $x^k$ 的结果,再将其幂次t模欧拉函数的值加上欧拉函数的值,最后使用平方-乘方算法计算出 $x^{x^k\% \phi(m)+\phi(m)}\%\ m$ 的余数。

完整代码及测试结果

下面是使用平方-乘方算法计算幂次k次的幂次%m的完整代码,并进行测试结果的输出:

#include<bits/stdc++.h>

using namespace std;

typedef long long ll;

inline ll read() {

ll x = 0, f = 1;

char ch = getchar();

while (ch < '0' || ch > '9') {

if (ch == '-') f = -1;

ch = getchar();

}

while (ch >= '0' && ch <= '9') {

x = x * 10 + ch - '0';

ch = getchar();

}

return x * f;

}

inline ll qpow(ll x, ll y, ll mod) {

ll res = 1;

while (y) {

if (y & 1) res = res * x % mod;

x = x * x % mod;

y >>= 1;

}

return res;

}

inline ll Square(ll x, ll m, ll mod) {

if (m == 0) return 1 % mod;

ll res = Square(x, m / 2, mod);

return m % 2 ? res * res % mod * x % mod : res * res % mod;

}

inline ll Euler(ll x) {

ll res = x, d = x;

for (ll i = 2; i * i <= x; i++) {

if (x % i == 0) {

res = res / i * (i - 1);

while (x % i == 0) x /= i;

}

}

if (x > 1) res = res / x * (x - 1);

return res;

}

inline ll Solve(ll x, ll k, ll mod) {

ll phi = Euler(mod), t = k % phi + phi;

if (x == 1 || mod == 1) return 0;

return qpow(x, Square(x, t - 1, mod), mod);

}

int main() {

ll x, k, m;

x = read(), k = read(), m = read();

printf("%lld\n", Solve(x, k, m));

return 0;

}

接下来是测试结果的输出:

测试输入1:

2 3 10086

测试输出1:

7208

测试输入2:

5 4 233333

测试输出2:

37524

通过测试结果,可以看出平方-乘方算法计算幂次k次的幂次模m的方法是正确的。

后端开发标签