题目链接:超级次方
只是看到题目,还没看内容就觉得是要用快速幂来玩,但是做完后感觉还是遍历简单
使用mod的性质:(a*b)%c=( a % c * b % c ) % c 来解决溢出
class Solution { public: //计算c的seconary次方 int pow1(int c, int secondary) { int res = 1; while (secondary) { res = ((res%1337)*(c%1337)) % 1337; //上述写法与res=(res*c)%1337一样,但 //比较好理解为什么不会溢出 secondary--; } return res; } int superPow(int a, vector<int>& b) { int res = 1; auto it = b.rbegin(); //将问题分开:每次只算一个位置的次方, //最后乘起来就好了 while (it != b.rend())//计算结果 { res = (res%1337*pow1(a, *it)%1337) % 1337; a = pow1(a, 10);//计算下一个次方的底数 it++; } return res; } };
代码比较朴实无华,pow1函数算的就是一个数的n次方,
这个解法妙的地方的是 a = pow1(a, 10); 这一行代码
对于
a:2
b:[1,1]
这个测试用例来说,我们的思路一般会是这样:
res=22222222222
但使用上面的算法是:
res=2 * (2 ^ 10) ^ 1
两者的区别在哪呢?
对于第一种算法,我们在b的1下标位置很明显知道是2的一次方,但是来到0下标的时候还是1次方吗?不是,是1*10^1=10,那机器要知道是几次方不就需要知道这是第几位,还需要保存一下次方嘛,那对于很大的数组就会有一个严峻的问题:没有足够大的类型保存次方。
那么第二种算法的思路:我不算你次方是多大,我算你底数是多大,因为数组每间隔一位,次方位数之间就相差1,我们可以思考利用一下这个性质:
2
[3,4,5,6]
第一次:2^ (1 * 6) == (21)^6
第二次:2^(10 * 5) == (210)^5
第三次:2^(10 * 10 * 4) == (2100)^4
第四次:2^(10 * 10 * 10 * 3) == (21000)^3
可以发现底数每次都以相同的规律增大,都变为上一次的10次方,因为我们写的pow1函数算的正好是求某一个数的n次方,在里面已经解决了溢出问题,所以 a = pow1(a, 10);这行代码就很容易理解了
对于溢出问题,因为mod的性质让我们可以对任何一个参与*的数字都取mod,所以我们可以在 *运算中对所有数都取mod,当然如果不想只需要在可能溢出的位置取mod即可!
感觉挺好玩的,就写了些和大家一起分享✨