快速幂题目总结

目录

前言

50.pow(x, n)

 方法一

直接调用Math类下的pow函数

 方法二

暴力法:逐个遍历

方法三

快速幂(分而治之) + 递归

方法解析

2961.双模幂运算

 分析

分解模 + 快速幂 + 递归

前言

本文为leetcode上的题目简单分析总结,仅作记录,欢迎提出建议,共同学习交流。


50.pow(x, n)

实现 pow(x, n) ,即计算 x 的整数 n 次幂函数(即,x^{n} )。

示例 1:

输入:x = 2.00000, n = 10
输出:1024.00000

示例 2:

输入:x = 2.10000, n = 3
输出:9.26100

示例 3:

输入:x = 2.00000, n = -2
输出:0.25000
解释:2-2 = 1/22 = 1/4 = 0.25

 方法一

直接调用Math类下的pow函数
class Solution {
    public double myPow(double x, int n) {
        return Math.pow(x, n);
    }
}

 方法二

暴力法:逐个遍历

时间复杂度:O\left (n \right )

对于数据较大的数时会超时,不推荐使用

class Solution {
    public double myPow(double x, int n) {
        double res = 1;
        // 处理n为负数的情况
        if(n < 0){
            x = 1/x; // 直接将x变为倒数
            n = -n;  // 同时把n变成正数
        }
        // 处理特殊情况
        for(int i=0; i<n; i++){
            res *= x; // 逐项相乘
        }
        return res; //返回答案
    } 
}

方法三

快速幂(分而治之) + 递归

时间复杂度:O\left (log n\right )

class Solution {
    public double myPow(double x, int n) {
        // 负指数幂则把n取负,最后的结果取倒数
        if(n < 0) return 1 / quickMul(x, -n);
        else return quickMul(x, n);
    }
    
    // 定义快速幂函数
    public double quickMul(double x, int n) {
        // 处理特殊情况
        if (n == 0) {
            return 1.0;
        }
        // 定义变量存储中间值
        double y = quickMul(x, n / 2);
        // 如果n为偶数,相除得到整数则只需y*y
        // 如果n为奇数,相除会向下取整,因而需要多乘一个x
        return n % 2 == 0 ? y * y : y * y * x;
    }
}
方法解析

「快速幂算法」的本质是分治算法

详解请观看:  . - 力扣(LeetCode)

如果我们要计算 x^{64} 我们可以按照:

x\rightarrow x^{2}\rightarrow x^{4}\rightarrow x^{8}\rightarrow x^{16}\rightarrow x^{32}\rightarrow x^{64}

的顺序,从 x 开始,每次直接把上一次的结果进行平方,计算 6 次就可以得到 x^{64} 的值,而不需要对 x 乘 63 次 x。

如果我们要计算 x^{77} 我们可以按照:

x\rightarrow x^{2}\rightarrow x^{4}\rightarrow x^{9}\rightarrow x^{19}\rightarrow x^{38}\rightarrow x^{77}

的顺序,在 x\rightarrow x^{2}x^{2}\rightarrow x^{4}x^{19}\rightarrow x^{38} 这些步骤中,我们直接把上一次的结果进行平方,而在 x^{4}\rightarrow x^{9}x^{9}\rightarrow x^{19}, x^{38}\rightarrow x^{77} 这些步骤中,我们把上一次的结果进行平方后,还要额外乘一个 x。

直接从左到右进行推导看上去很困难,因为在每一步中,我们不知道在将上一次的结果平方之后,还需不需要额外乘 x。但如果我们从右往左看,分治的思想就十分明显了:

  • 当我们要计算 x^{n} 时,我们可以先递归地计算出 y=x^{\frac{n}{2}} ,其中 \frac{n}{2} 进行向下取整;
  • 如果 n 为偶数,那么 x=y^{2}
  • 如果 n 为奇数,那么 x=y^{2} * x
  • 递归的边界为 n=0,任意数的 0 次方均为 1。

由于每次递归都会使得指数减少一半,因此递归的层数为 O(logn),算法可以在很快的时间内得到结果。

 下面引入相同类型的题目:

2961.双模幂运算

给你一个下标从 开始的二维数组 variables ,其中 variables[i] = [ai, bi, ci, mi],以及一个整数 target 。

如果满足以下公式,则下标 i 是 好下标

  • 0 <= i < variables.length
  • ((aibi % 10)ci) % mi == target

返回一个由 好下标 组成的数组,顺序不限 。

示例 1:

输入:variables = [[2,3,3,10],[3,3,3,1],[6,1,1,4]], target = 2
输出:[0,2]
解释:对于 variables 数组中的每个下标 i :
1) 对于下标 0 ,variables[0] = [2,3,3,10] ,(23 % 10)3 % 10 = 2 。
2) 对于下标 1 ,variables[1] = [3,3,3,1] ,(33 % 10)3 % 1 = 0 。
3) 对于下标 2 ,variables[2] = [6,1,1,4] ,(61 % 10)1 % 4 = 2 。
因此,返回 [0,2] 作为答案。

示例 2:

输入:variables = [[39,3,1000,1000]], target = 17
输出:[]
解释:对于 variables 数组中的每个下标 i :
1) 对于下标 0 ,variables[0] = [39,3,1000,1000] ,(393 % 10)1000 % 1000 = 1 。
因此,返回 [] 作为答案。 

 分析

模运算恒等式:

        (a * b) mod m=((a mod m) * (b mod m)) mod m

这里模运算可以分解为快速幂,再次运用上式得到多个部分放入一个方法中,在主函数中调用,满足此条件则把此下标添加到 list 中。

分解模 + 快速幂 + 递归
class Solution {
    public List<Integer> getGoodIndices(int[][] variables, int target) {
        List<Integer> list = new ArrayList<>();
        for (int i = 0; i < variables.length; i++) {
            int[] v = variables[i];
            // 满足条件则添加
            if (powMod(powMod(v[0], v[1], 10), v[2], v[3]) == target) {
                list.add(i);
            }
        }
        return list;
    }

    public int powMod(int x, int y, int mod){
        int res = 1;
        while(y != 0){
            // 查看最后一位是否为1
            if((y & 1) == 1)
                // 乘相应阶数同时取模
                res = res * x % mod;
            // x的阶数翻倍同时取模
            x = x * x % mod;
            // y右移一位
            y = y >> 1;
        }
        return res;
    }
}

 

  • 27
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值