标签:
问题描述:求一个整数的n次幂,对m取余。
1 int Pow(int num, int n, int m)
2 {
3 int res = 1;
4
5 while (n--) {
6 res *= num;
7 }
8
9 return res % m;
10 }
以上代码有个隐患,当n足够大的时候,res会溢出,因此需要优化。
有这样一个公理:两个数的积对另一个数取余,等于这两个数分别对第三个数取余的乘积再对第三个数取余。也就是a*b%c = (a%c)*(b%c)%c
根据以上公理,可以对上面的代码进行优化:
1 int Pow(int num, int n, int m)
2 {
3 int res = 1;
4
5 while (n--) {
6 res *= num;
7 res %= m; // 增加的代码
8 }
9
10 return res % m;
11 }
思路一的时间复杂度为O(n),我们想更快一点,就需要再次优化。
我们知道,a^n = a^(n/2) * a^(n/2) (n为偶数),a^n = a^(n/2) * a^(n/2) * a (n为奇数)
因此,如果要求a^n,只需要求得a^(n/2),根据此理论,可写出如下代码,可将时间复杂度优化为O(logn):
1 int Pow(int num, int n, int m)
2 {
3 if (n == 1) {
4 return num % m;
5 }
6
7 int tmp = Pow(num, n/2, m);
8 if (n % 2 == 0) // n为偶数
9 {
10 return (tmp * tmp) % m;
11 }
12 else { // n为奇数
13 return (tmp * tmp * num) % m;
14 }
15 }
思路二中,我们采用了递归求a^(n/2),成功将时间复杂度优化到O(logn),但是递归求解会产生很多函数调用,使得效率不是看上去的那么高,我们一般优化递归的做法是将递归写为递推。
将n写为二进制,以n=11为例:a^n = a^11 = a^(2^0 + 2^1 + 2^3)
这样,根据以上推理,可将思路二中的递归写为递推:
1 int Pow(int num, int n, int m)
2 {
3 int res = 1;
4
5 // 把n的二进制数从低位到高位遍历,如果为1,则乘以num,
6 // 每循环一次,num变为之前的平方,n变为之前的一半
7 while (n) {
8 // 二进制的最后一位为1,是原始n的第几个1,就乘以num^(2的几次幂)
9 if (n & 1 == 1) {
10 res *= (num % m);
11 }
12 num *= num;
13 n >>= 1;
14 }
15
16 return res % m;
17 }
标签:
原文地址:http://www.cnblogs.com/mhscn/p/4700151.html