快速幂
快速幂是个很常用的算法,思想简单,但对于博主这种新手渣渣来说,实现起来总是丢三落四,原因还是没有对原理的透彻理解。因此写下这篇随笔,为了让新手更好地记住快速幂算法。
如果求一个数a的n次方,我们常规的方法便是相乘n-1次,因此复杂度是O(n)。而快速幂的精髓则在于乘数不再永远是a。举个栗子,计算a的平方,我们可以直接计算a*a;计算a的四次方呢?a*a*a*a吗?NO!!!
不过我们可以聪明地想到:
①计算a*a,得到a2的结果
②计算a2*a2,得到a4的结果
同理,如果我们要计算a8、a16...等等,便可以利用前一次乘积得到的结果作为乘数进行平方。不过这并不是快速幂的算法思路,博主就是因为这种固定化思维残害了。试想如果我们要计算a17、a31...那么我们还能用前一次的乘法运算结果作为乘数进行迭代的平方运算吗?很明显不能,因为17和31不属于2,4,8,16...这个等比序列中。乘数既不是a,也不是每次乘法运算结果的平方迭代,所以我们的乘数到底应该是什么呢?其实这种思路已经和快速幂的思想十分得接近了,只是差了一个序列筛选的过程。
让我们以a17为例。17的二进制表示法是10001(2)= 1*20+0*21+0*22+0*23+1*24。因为a17=a10001(2) ,大家都知道幂的加法相当于底数的幂的乘积,即Ai+j = Ai * Aj 。这时我们注意到幂:20 21 22 23 24...这个序列不就是上面0,2,4,8,16...这个等比序列吗?而这些幂序列的系数就对应着原幂数二进制表示的位,它只有两种选择:0/1。当系数为1时,我们就对应筛选出相应的幂,系数为0则忽略。于是我们可以得到下面的代码:
1 int quickPow(int a, int b) //a是底数,b是幂 2 { 3 int ans = 1; 4 while(b != 0) 5 { 6 if(b & 1 != 0) //说明幂的系数为1,选择它 7 ans *= a; 8 a *= a; //幂序列移到下一位 9 b >>= 1; //幂序列的系数移到对应的下一位 10 } 11 }
到此,快速幂的所有思路便已经讲完了。在实际应用中幂数的数量级经常在千万或亿级以上,使用快速幂能将复杂度为O(n)的指数运算瞬间降到O(logn)。另外应该注意答案的数据范围,使用long long 还是使用 int, 避免发生溢出。
拓展:将快速幂运用于矩阵的幂运算
思路很简单,只要将实数乘法运算用矩阵的乘法运算函数代替即可。初始化为1的实数可以用单位矩阵代替。最经典的运用就是斐波那契数列!当n数量级特别大时,直接用递归或迭代将会超时,这时就要用到矩阵算法去求斐波那契数列的第n项了。具体的代码博主不会给出来,只有自己亲自打出来的代码才能算是领悟到算法的思想精髓(其实是因为博主很懒啦~~~)。
斐波那契数列矩阵算法:
其他参考网址:http://www.cnblogs.com/CXCXCXC/p/4641812.html