快速幂及其实战

快速幂

快速幂是一种能够对m的n次幂快速进行计算的一种算法。能在log(n)的时间内求出结果。

传统算法

一般情况下我们会想到直接对mn次不就得到了m的n次幂了吗?也就是下面的这种算法,我们可以看到它的时间复杂度是O(n)

private static double power1(int a,int b){
        double res = 1;
        for (int i = 0; i < b ; i++){
            res *= a;
        }
        return a == 0 ? 0 : res;
}

如果b足够大,很容易超时。

递归快速幂

递归快速幂的思想大致如下:

2^15可以分解成求2^7 * 2^7 * 22^7分解为2^3 * 2^3 * 2

2^3分解为2^1 * 2^1 * 2,当幂次为1的时候就是递归的边界条件,我们直接return。我们可以看到2^15依赖2^7的计算,2^7依赖2^3

所以我们可以用递归来实现它

private static double powers(int a, int b) {
    if(b == 1) {
        return a;
    }
    if((b & 1) == 1) {
        double temp = powers(a, b/2);
        return temp * a * temp;
    } else {
        double temp = powers(a,b/2);
        return temp * temp;
    }
}

总结:

递归快速幂就是指将b不停的分割,从而进行简化操作

  • 如果是奇数就变成(a ^ (b/2)) * (a ^ (b/2)) * a
  • 如果是偶数的话就变成了(a ^ (b/2)) * (a ^ (b/2))
  • 一直往后进行划分,直到b变成1为止。

非递归快速幂

非递归快速幂的思想大致如下:

我们通过递归快速幂可以看到不断除以2,是不是跟右移动一位是一样的?所以我们换个角度,从n这个幂次的二进制出发,例如2^42,我们可以将42化成101010的二进制。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-WaqYuTvw-1649760308667)(%E5%89%91%E6%8C%87Offer.assets/1649757225596.png)]

上述式子我们是很容易知晓的,指数相加。可以看到我们把101010拆成了1000001010,这是因为我们在计算的过程中,会先计算m * m == m^2=m^10(2),然后再计算(m * m) * (m * m) == m^4m^100(2)m^8m^1000(2)不就是对指数在移位吗?有多少位二进制就计算几次。

所以像计算2^100000(2)这种我们只需要移动100000(2)的长度6就行了,每次将上次的结果*结果得到新结果,幂次就会 * 2啦,我们知道* 2是会后面补0的,所以自然就慢慢得到100000,那么新结果这个时候就是我们2^100000(2),首先2^1 * 2^1 变成了2^10(2),然后2^2*2^2变成了2^100(2),最后通过5次这样的计算我们就可以算到2^100000(2)

回归正题,我们还有2^1010(2)没求呢?2^1010(2)分解成2^1000(2) * 2^10(2)2^1000(2)2^10(2)我们是不是在上述求2^100000(2)的过程中会有计算到2^1000(2)2^10(2)的步骤呢?所以我们只需要在计算到这的时候,记录一下当前的值就行了。根据我们的拆分规则,以为1为分界点的,101010(2)100000(2) + 1000(2) + 10(2)具体可以回归到上图观察下。每次我们遇到位数为1的时候就需要记录一下,以便于贡献给下次为1的时候计算。具体代码如下:

private static double power3(int a, int b) {
    double res = 1;
    double base = a;
    whlie(b > 0) {
        if((b & 1) == 1) {
            //res是保存的数据,就比如上面说的,1010分为1000和10,我们最后要合并的,也就
            //是1000 + 10,当我们第一次调用这个方法的时候就是在res中保存了一个10的结果
            //当我再次调用的时候res = res * base  此时base是1000的结果,res是10的结果,*就是相加
            res *= base;
        }
        base *= base;
        b >>= 1;
    }
}

当我们进行运算速度测试时,我们可以看到传统算法为11,而其他两种为0,在数据量特别大的时候,这种对比是特别明显的。传统的为O(n),快速幂为O(logn),所以为什么有这么大差别很明显了。


实战

在这里插入图片描述
正片开始

这道题说了我们的n可能会存在负数,所以为了防止溢出,我们要给n的类型变为long类型,其余的和上面的一致。

public double myPow2(double x, int n) {
    boolean is_minus = n < 0;
    //这里需要注意越界问题
    long i = n;
    long m = Math.abs(i);
    double res = 1;
    while(m > 0) {
        if ((m & 1) == 1) {
            res *= x;
        }
        x *= x;
        m >>= 1;

    }
    if (is_minus) {
        res = 1 / res;
    }
    return res;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值