题目:求(a^n)%p的值。
方案一:暴力解法,先算出a^n的值,然后再去求模。
分析:这种做法最简便直观,但缺点是运算性能不好,在输入较大时会产生溢出,导致结果错误。
代码:
public static long calMod1(int a, int n, int p) {
// 暴力解法1
long sum = 1;
for (int i = 0; i < n; i++)
sum *= a;
return sum % p;
}
方案二:暴力解法,循环求a^n的每一步中,将求模运算融入其中。
分析:可读性上也是比较直观,并且不会溢出。
代码:
public static long calMod2(int a, int n, int p) {
// 暴力解法2
long sum = 1;
for (int i = 0; i < n; i++)
sum = sum * a % p;
return sum % p;
}
方案三:在a^n的运算中,随着n的增大,模p的结果最多有p种结果,并且是循环出现的。所以,我们记录其中不断出现的结果,直到有重复的出现(证明一个轮回了)。
分析:性能最优,并且不会溢出。
代码:
public static long calMod3(int a, int n, int p) {
// 循环方法
if (a % p == 0)
return 0;
int count = 0;
long yushu = a % p;
long[] array = new long[p];
array[0] = yushu;
while (count < n) {
yushu = yushu * a % p;
if (yushu == array[0])
break;
else
array[++count] = yushu;
}
return array[n % p - 1];
}
方案四:运用数学原理:(a*b)%p == (a%p)*(b*p)%p,将a^n不断的拆分成两个部分。
分析:时间复杂度上为O(logN),但因为运用了递归,所以总体性能不如方案三。
代码:
public static long calMod4(int a, int n, int p) {
// 采用二分
if (p == 1)
return 0;
if (n == 0)
return 1;
if (n % 2 == 0)
return calMod4(a, n / 2, p) * calMod4(a, n / 2, p);
else
return calMod4(a, n / 2, p) * calMod4(a, n / 2, p) * (a % p) % p;
}