理解快速幂运算并进行应用
快速幂运算的解释
问n是否满足$x^n \mod n = x (1 < x < n)$?
先由一个例子引入:
$3^{11} = 3 \times 9^5 = 3 \times 9 \times 81^2 = 3 \times 9 \times 6561^1$
$result = 3 \times 9 \times 6561 = 3^{2^0} \times 3^{2^1} \times 3^{2^3}$
可见发现这次运算中,幂的结果等于变化中所有当指数为奇数时底数之积。其中,每次运算均发生指数除二(对应二进制右移一位),且当该指数为奇数时,原式乘上底数。
而这个过程其实相当于一个数进行模2取余求二进制数的过程,每次都除2,当模2余1,即对应二进制最末位为1时乘上底数,则由此可以推知快速幂运算的算法过程。
这个结论是可以证明的,如下:
对于任何十进制正整数n,设其对应二进制数为"$b_m…b_3b_2b_1$",则有:
- 二进制转十进制:$n = 1b_1+2b_2+4b_3+…+2^{m-1}b_m$;
- 幂的二进制展开:$x^n = x^{1b_1}x^{2b_2}x^{4b_3}…x^{2^{m-1}b_m}$。
则对于$x^n$的求解,可以转化为:
- 计算$x^1,x^2,x^4…x^{m-1}$的值,相当于$x=x^2$的过程;
- 获取二进制各位$b_1,b_2,b_3,…,b_m$的值,相当于模2求余的过程。
上述过程中,当$b_i=0$时,$x^{2^{i-1}b_i}=1$,反之为$x^{2^{i-1}}$,由此可以顺利计算$x^n$。
相应代码:
1typedef long long ll;
2ll mod_pow(ll x, ll n, ll mod) {
3 ll res = 1;
4 while(n > 0) {
5 if(n & 1 == 1) res = res * x % mod; // 一个数&1的结果就是取该数二进制的最末位
6 x = x * x % mod;
7 n >>= 1;
8 }
9 return res;
10}
注意,运用位运算可以提高效率!
一道易错题
剑指 Offer 16. 数值的整数次方
实现 pow(x, n) ,即计算x的n次幂函数。不得使用库函数,同时不需要考虑大数问题。
其实就是快速幂运算的简单应用,然而却很容易忽略一些细节:
可以看几个判例:
11.00000
2-2147483648
不特判的话有可能超时,注意当$x=1,x=-1,x=0,n=0$时都可以直接得到答案。
同时,如果执行n=-n
,将会出错,因为2147483648超出了int的范围[-2147483648, 2147483647]!可以通过long n1 = n
解决这个问题。
12.10000
23
应该注意到x可以为小数。
代码实现:
1class Solution {
2public:
3 double myPow(double x, int n) {
4 if (x == 0) return 0;
5 if (x == 1 || n == 0) return 1;
6 if (x == -1) return n % 2 ? -1 : 1;
7 long n1 = n; double ans = 1.0;
8 if (n < 0) {
9 x = 1 / x;
10 n1 = -n1;
11 }
12 while (n1) {
13 if (n1 & 1) ans *= x;
14 x = x * x;
15 n1 >>= 1;
16 }
17 return ans;
18 }
19};