双模幂运算

2024-07-30

题目:

给你一个下标从 0 开始的二维数组 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 。
因此,返回 [] 作为答案。

提示:

  • 1 <= variables.length <= 100
  • variables[i] == [ai, bi, ci, mi]
  • 1 <= ai, bi, ci, mi <= 103
  • 0 <= target <= 103

最极致的模拟,最简单的思路

思路:

直接模拟运算过程就可以了

代码:

class Solution {
public:
    vector<int> getGoodIndices(vector<vector<int>>& variables, int target) {
        vector<int> ans;
        for (int i = 0; i <variables.size(); i++) {
            int a = variables[i][0], b = variables[i][1], c = variables[i][2], m = variables[i][3];
            int temp = 1;
            for (int j = 0; j < b; j++) {
                temp = (temp * a) % 10;
            } 
            int tmp = 1;
            for (int j = 0; j < c; j++) {
                tmp = (tmp * temp) % m;
            }
            if (target == tmp) {
                ans.push_back(i);
            }
        }
        return ans;
    }
};

快速幂,加速运算

思路:

按题意扫描一遍 variables 数组进行模拟,找到满足要求的下标即可。在模拟时,使用了快速幂算法来计算幂运算。

「快速幂算法」的本质是分治算法。
举个例子,如果我们要计算 x^64 ,我们可以按照:x → x^2 → x^4 → x^8 → x^16 → x^32 → x^64的顺序,从 x 开始,每次直接把上一次的结果进行平方,计算 6 次就可以得到 x^64 的值,而不需要对 x 乘 63 次 x。

再举一个例子,如果我们要计算 x^77 ,我们可以按照:x → x^2 → x^4 → x^9 → x^19 → x^38 → x^77 的顺序,在 x → x^2,x^2 → x^4 ,x^19 → x^38 这些步骤中,我们直接把上一次的结果进行平方,而在 x^4 → x^9,x^9 → x^19,x^38 → x^77 这些步骤中,我们把上一次的结果进行平方后,还要额外乘一个 x。

直接从左到右进行推导看上去很困难,因为在每一步中,我们不知道在将上一次的结果平方之后,还需不需要额外乘 x。但如果我们从右往左看,分治的思想就十分明显了:
	当我们要计算 x^n 时,我们可以先递归地计算出 y = x^⌊n/2⌋,其中 ⌊a⌋ 表示对 a 进行下取整;
	根据递归计算的结果,如果 n 为偶数,那么 x^n = y^2;如果 n 为奇数,那么 x^n = y^2 * x;
	递归的边界为 n=0,任意数的 0 次方均为 1。
由于每次递归都会使得指数减少一半,因此递归的层数为 O(logn),算法可以在很快的时间内得到结果。

代码:

class Solution {
public:
    int pow_mod(int x, int y, int mod) {
        int res = 1;
        while (y) {
            if (y & 1) {
                res = res * x % mod;
            }

            x = x * x % mod;
            y >>= 1;
        }   
    return res;
    }
    
    vector<int> getGoodIndices(vector<vector<int>>& variables, int target) {
        vector<int> ans;
        
        for (int i = 0; i < variables.size(); i++) {
            auto &v = variables[i];
            if (pow_mod(pow_mod(v[0], v[1], 10), v[2], v[3]) == target) {
                ans.push_back(i);
            }
        }
        return ans;
    }
};