快速幂取模运算

由于数学基础比较薄弱,最近在做到一个算法题时,才了解到“快速幂”这一概念。

题目地址:https://leetcode-cn.com/problems/jian-sheng-zi-ii-lcof/

怎么样去理解“快速幂”?

假设现在我接到一个需求:算出2的64次方。

怎么做最快、最容易呢,总不可能真的算24次吧。通常我们会这么做

2^64 = 2^32 * 2^32

2^32 = 2^16 * 2^16

2^16 = 2^8 * 2^8

2^8 = 2^4 * 2^4

2^4 = 2^2 * 2^2

2^2  = 4

不断地削减指数,通过最小的运算次数,我们可以比较轻松地算出最后的结果。

其实这事放到计算机身上,也一样。计算机也不喜欢做太多的乘法运算(耗时),相比较多次计算,它更喜欢参与运算的数大点,长痛不如短痛。

例如计算3的10001次方,会按照下面的流程来计算。

如果指数是偶数,则直接拆分成另外两个指数。

如果是计数,则会多一个3出来,算总乘积时,也不能把这个3给漏了。

模运算

“指数爆炸”这一概念想必大家都听过,如果指数过大,那么运算的结果很容易超过Integer.MAX_VALUE 或者 Long.MAX_VALUE。
所以一般类似的算法题会要求 对最后的结果取模。模运算的一些定律我们还是要了解一点的,本篇文章用到:

(a * b) % c = ( a % c * b % c) % c

代码实现

定义一个接口,规定入参和返回值

public interface Pow {
    /**
     * 指数运算
     * @param base 底数
     * @param index 指数
     * @param mod 取模
     * @return 结果
     */
    long calculate(long base, long index, int mod);
}

常规算法实现

public class Pow1 implements Pow{
    @Override
    public long calculate(long base, long index, int mod) {
        long res = 1;
        int count = 0;
        while (index-- > 0) {
            res = (res * base) % mod;
            count++;
        }
        System.out.println("常规方法-运行次数 " + count);
        return res % mod;
    }
}

快速幂实现

res用来保存结果

如果指数为奇数,则乘以base,把这个多余的base算进结果里。

如果是偶数,则直接扩大base。

直到最后,指数也为1了,就可以把最后得到的base汇总到res里,并返回结果res。

public class Pow2 implements Pow {
    @Override
    public long calculate(long base, long index, int mod) {
        long res = 1;
        int count = 0;
        while (index > 0) {
            if(index % 2 == 1) {
                index--;
                res = res * base % mod;
            }
            index = index / 2;
            base = base * base % mod;
            count++;
        }
        System.out.println("快速幂-运行次数 " + count);
        return  res;
    }
}

测试类

class Solution {
    public static void main(String[] args) {

        Solution solution = new Solution();
        solution.cost("常规方法", new Pow1());
        solution.cost("快速幂", new Pow2());
    }

    public void cost(String name, Pow pow) {
        long before = System.currentTimeMillis();
        long res = pow.calculate(3, 895556423, 1000);
        System.out.println(name + "-结果是 " + res);
        long end = System.currentTimeMillis();
        System.out.println(name + " 耗时 " + (end - before) / 1000.0);
    }
}

运行结果

结论

上述结果可以看到,当指数为亿级时,如果真的做上亿次乘法,则将消耗12.5秒左右的时间。

但如果用快速幂不断地削减指数,则耗费的时间几乎等于0,可见优秀的算法带来的性能提升之大。

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值