常见算法之12---求a^n%p

题目:求(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;
    }
原文地址:https://www.cnblogs.com/xiaoChongUp/p/3337562.html