zoukankan      html  css  js  c++  java
  • 深入理解非递归快速幂

    深入理解非递归快速幂

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

    原理

    先举个栗子,求 (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循环结束。

    以上。

    参考

    本文采用 知识共享 署名-非商业性使用-相同方式共享 3.0 中国大陆 许可协议进行许可。欢迎转载,请注明出处: 转载自:Santiego的博客

  • 相关阅读:
    WTL自绘表指针
    QQ日志搬家工具 1.0
    一个消失很久的首页
    共享.net PE文件结构浏览器+名称混淆 + 16进制结构图 (源码)
    JavaScript中Array 对象相关的几个方法
    ASP.NET底层架构
    ASP.NET 2.0 正式版中无刷新页面的开发
    ASP.NET组件设计Step by Step(9)
    ASP.NET组件设计Step by Step(8)
    ajax上传(xmlhttp上传文件突破大小限制)
  • 原文地址:https://www.cnblogs.com/santiego/p/9521181.html
Copyright © 2011-2022 走看看