码迷,mamicode.com
首页 > 其他好文 > 详细

深入理解非递归快速幂

时间:2018-08-23 02:03:51      阅读:177      评论:0      收藏:0      [点我收藏+]

标签:not   观察   x的n次方   规则   math   过程   https   分解   lin   

深入理解非递归快速幂

网络上很多博客都只有板子,并没有严谨详细探讨其思想。本篇文章将从二进制角度来深入理解非递归快速幂

原理

先举个栗子,求 \(3^{15}\) 。对于 \(3^{15}\) 我们可以通过数学幂的运算法则得到下面的式子

\[ 3^{17}=3^{2^{4}+2^{0}}=3^{2^{4}}*3^{2^{0}} \]

\[ 3^{2^{i}}=3^{2^{i-1}}*3^{2^{i-1}} \]
我们会发现将其指数化为以2为底数的数之和后,计算 \(3^{15}\) 时,仅需算出 \(3^{2^{4}}\)\(3^{2^{0}}\) 便可求得 \(3^{15}\) 的值,相比于遍历指数次,每次乘以底数的传统做法,效率更高。而我们又会发现 \(3^{2^{i}}\) 是由 \(3^{2^{i-1}}\) 转换过来的,所以,我们只需求出之和构成指数的2为底数的各个数(即问是哪些以2为底数的数累加起来为指数的),便可求解。
\[ 17=2^{4}+2^{0} \]
而后,我们再通过观察发现,上面这个式子中的4和0其实就是17的二进制上不为0的二进制位

技术分享图片

(废话,二进制本身不就是将任意十进制数拆解为以2为底数的数之和的过程吗)

找出之和构成指数的以2为底数的各个数后,我们只需要从 \(3^{2^{0}}\) 开始,每次平方本身,推出第i位的 \(3^{2^{i}}\) ,并根据上面的分解规则(规律),逐位判断二进制从而解决最后的问题从而最终求出快读幂。

总而言之,我觉得快速幂算法其实是利用二进制将数分解为很多个递归平方(词穷了),每次都以平方级增长,从而实现快速求幂。

实现

先上个非递归快速幂板子

LL pow_mod(LL a, LL b, LL mo){
    LL ret = 1;
    while(b){
        if(b & 1) ret = (ret * a) % mo;
        a = (a * a) % mo;
        b >>= 1;
    }
    return ret;
}

首先需要明白的是,b&1在二进制中可以理解为取b的二进制最后一位,如果b&1返回1(即true)则末尾为1,反之为0(所以根据这个性质也可以很快地判断一个数是否为奇数),所以在这里只有当b的二进制最后一位为1时,才执行其后语句ret = (ret * a) % mo;;另外b >>= 1是表示将b右移1位,即将b的二进制整体向右移动一格。

技术分享图片

既然理解了原理以及实现要点,希望读者尽可能自己琢磨一下这个程序,可以模拟感受一下,因为那样来得更快

在这个程序中,每一次,无论当前二进制最后一位是否为1,a都要平方一下(因为 \(3^{2^{i}}=3^{2^{i-1}}*3^{2^{i-1}}\) ,前一个值会影响后一个值,所以必须维护),然后将当前二进制右移一位,去掉已经处理过的二进制数位以处理下一位,但当当前二进制最后一位为1时,根据之前的规律,需将a加入到答案中,最终当所有二进制数位都被处理掉后,b自然变为0,while循环结束。

以上。

参考

深入理解非递归快速幂

标签:not   观察   x的n次方   规则   math   过程   https   分解   lin   

原文地址:https://www.cnblogs.com/santiego/p/9521181.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!