算法笔记学习---快速幂

简介: 算法笔记学习---快速幂

先来看一个问题:

给定三个正整数a、b、m (a<10, b<10,1<m<10),求ab%m。

这只要学过循环就能写出来了,就像下面的代码,时间复杂度是O(b):

typedef long long LL;
LLpow(LL a , LL b , LL m)
{
    LL ans = 1;
    for(int i=0;i<b;i++)
    {
        ans = ans * a % m;
    }
    return ans;
}

代码中使用long long而不用int的原因是防止两个int型变量相乘后溢出。

接下来研究一个更进一步的问题:

给定三个正整数a、b、m (a<103, b<1018, 1<m<109),求ab%m。

对这个问题,如果还是按上面的做法显然是不行的,O(b)的复杂度连支持b< 108都已经很艰难了,更何况1018。

这里要使用快速幂的做法,它基于二分的思想,因此也常称为二分幕。快速幂基于以下事实:

①如果b是奇数,那么有ab=a * a(b-1).
②如果b是偶数,那么有ab=ab/2 * ab/2.

显然,b是奇数的情况总可以在下一步转换为 b是偶数的情况,而b是偶数的情况总可以在下一步转换为b/2的情况。这样,在log(b)级别次数的转换后,就可以把b变为0,而任何正整数的0次方都是1。

举个例子,如果需要求2^10:
①对210来说,由于幂次10为偶数,因此需要先求25,然后有210=25 * 25。
②对25来说,由于幂次5为奇数,因此需要先求24,然后有25 =2 * 24。
③对24来说,由于幂次4为偶数,因此需要先求22,然后有24=22 * 22。
④对22来说,由于幂次2为偶数,因此需要先求21,然后有22=21 * 21。
⑤对21来说,由于幂次1为奇数,因此需要先求20, 然后有21=2 * 20。
⑥20=1,然后从下往上依次回退计算即可。

这显然是递归的思想,于是可以得到快速幂的递归写法,时间复杂度为O(logb):

typedef long long LL;
//求a^b % m , 递归写法
LL binaryPow(LL a , LL b , LL m)
{
    if(b==0) return 1;       //如果b为0,那么a^0=1
    //b为奇数,转换为b-1
    if(b % 2 ==0) return a * binaryPow(a , b-1 , m) % m;
    else   //b为偶数,转换为b/2
    {
        LL mul = binaryPow(a , b/2 , m);
        return mul * mul % m;
    }
}

   上面的代码中,条件if(b %2 == 1)可以用if(b& 1)代替。这是因为b& 1进行位与操作,判断b的末位是否为1,因此当b为奇数时b&1返回1, if条件成立。这样写执行速度会快一点。

   还要注意,当b%2==0时不要返回直接返回binaryPow(a, b/2, m) binaryPow(a, b/2,m),而应当算出单个binaryPow(a, b/2, m)之后再乘起来。这是因为前者每次都会调用两个binaryPow函数,导致复杂度变成O(2^log(b)) = O(b)。例如求binaryPow(8)时, 会变成binaryPow(4) binaryPow(4),而这两个binaryPow(4)都会各自变成binaryPow(2) binaryPow(2),于是就需要求四次binaryPow(2);而每个biaryPow(2)又会变成binaryPow(1) binaryPow(1),因此最后需要求八次binaryPow(1)。

另外,针对不同的题目,可能有两个细节需要注意:
①如果初始时a有可能大于等于m,那么需要在进入函数前就让a对m取模。
②如果m为1,可以直接在函数外部特判为0,不需要进入函数来计算(因为任何正整数对1取模一定等于0)。

接下来研究一下快速幂的迭代写法。

   对ab来说,如果把b写成二进制,那么b就可以写成若干二次幂之和。例如13的二进制是1101,于是3号位、2号位、0号位就都是1,那么就可以得到13=23+22 +20=8+4+1,所以a13=a8+4+1=a8 a4 a0。

   通过上面的推导,我们发现a13可以表示成a8、a4、a0 的乘积。很容易想象,通过同样的推导,我们可以把任意的ab表示成a2k、… 、a8、a4、a2、a1中若干项的乘积,其中如果b的二进制的i号位为1,那么项a2i就被选中。于是可以得到计算ab的大致思路:令i从0到k枚举b的二进制的每一 位,如果当前位为1,那么累积a2i。注意到序列a2k、… 、a8、a4、a2、a1的前一项总是等于后一项的平方,因此具体实现的时候可以这么做:
①初始令ans等于1,用来存放累积的结果。
②判断b的二进制末尾是否为1 (即判断b& 1是否为1,也可以理解为判断b是否为奇数),如果是的话,令ans乘上a的值。
③令a平方,并将b右移一位(也可以理解为将b除以2)。
④只要b大于0,就返回②。
下面把上面的伪代码写成下面的样子,也就是快速幂的迭代写法:

//c/c++
typedef long long LL;
//求a^b % m , 迭代写法
LL binaryPow(LL a , LL b , LL m)
{
    LL ans = 1;
    while(b > 0)
    {
        if(b & 1)
        {    //如果b的二进制末尾为1(也可以写成if(b % 2))
            ans = ans * a % m;  //令ans累积上a
        }
        a = a * a % m;  //令a平方
        b >>= 1;  //将b的二进制右移1位,即b = b >> 1 或 b = b / 2
    }
    return ans;
}
#python
def qpow(a,b,mod):
    ret = 1
    while b:
        if(b&1):
             ret = ret*a % mod
        a = a*a % mod
        b>>=1
    return ret
相关文章
|
6天前
|
算法 JavaScript 前端开发
第一个算法项目 | JS实现并查集迷宫算法Demo学习
本文是关于使用JavaScript实现并查集迷宫算法的中国象棋demo的学习记录,包括项目运行方法、知识点梳理、代码赏析以及相关CSS样式表文件的介绍。
第一个算法项目 | JS实现并查集迷宫算法Demo学习
|
10天前
|
XML JavaScript 前端开发
学习react基础(1)_虚拟dom、diff算法、函数和class创建组件
本文介绍了React的核心概念,包括虚拟DOM、Diff算法以及如何通过函数和类创建React组件。
15 2
|
2月前
|
机器学习/深度学习 人工智能 资源调度
【博士每天一篇文献-算法】连续学习算法之HAT: Overcoming catastrophic forgetting with hard attention to the task
本文介绍了一种名为Hard Attention to the Task (HAT)的连续学习算法,通过学习几乎二值的注意力向量来克服灾难性遗忘问题,同时不影响当前任务的学习,并通过实验验证了其在减少遗忘方面的有效性。
49 12
|
2月前
|
算法 Java
掌握算法学习之字符串经典用法
文章总结了字符串在算法领域的经典用法,特别是通过双指针法来实现字符串的反转操作,并提供了LeetCode上相关题目的Java代码实现,强调了掌握这些技巧对于提升算法思维的重要性。
|
2月前
|
算法 NoSQL 中间件
go语言后端开发学习(六) ——基于雪花算法生成用户ID
本文介绍了分布式ID生成中的Snowflake(雪花)算法。为解决用户ID安全性与唯一性问题,Snowflake算法生成的ID具备全局唯一性、递增性、高可用性和高性能性等特点。64位ID由符号位(固定为0)、41位时间戳、10位标识位(含数据中心与机器ID)及12位序列号组成。面对ID重复风险,可通过预分配、动态或统一分配标识位解决。Go语言实现示例展示了如何使用第三方包`sonyflake`生成ID,确保不同节点产生的ID始终唯一。
go语言后端开发学习(六) ——基于雪花算法生成用户ID
|
2月前
|
存储 机器学习/深度学习 算法
【博士每天一篇文献-算法】连续学习算法之HNet:Continual learning with hypernetworks
本文提出了一种基于任务条件超网络(Hypernetworks)的持续学习模型,通过超网络生成目标网络权重并结合正则化技术减少灾难性遗忘,实现有效的任务顺序学习与长期记忆保持。
34 4
|
2月前
|
存储 机器学习/深度学习 算法
【博士每天一篇文献-算法】连续学习算法之RWalk:Riemannian Walk for Incremental Learning Understanding
RWalk算法是一种增量学习框架,通过结合EWC++和修改版的Path Integral算法,并采用不同的采样策略存储先前任务的代表性子集,以量化和平衡遗忘和固执,实现在学习新任务的同时保留旧任务的知识。
74 3
|
2月前
|
存储 机器学习/深度学习 算法
【博士每天一篇文献-综述】基于脑启发的连续学习算法有哪些?附思维导图
这篇博客文章总结了连续学习的分类,包括经典方法(重放、正则化和稀疏化方法)和脑启发方法(突触启发、双系统启发、睡眠启发和模块化启发方法),并讨论了它们在解决灾难性遗忘问题上的优势和局限性。
30 2
|
3月前
|
机器学习/深度学习 数据采集 算法
Python实现ISSA融合反向学习与Levy飞行策略的改进麻雀优化算法优化支持向量机回归模型(SVR算法)项目实战
Python实现ISSA融合反向学习与Levy飞行策略的改进麻雀优化算法优化支持向量机回归模型(SVR算法)项目实战
110 9
|
3月前
|
机器学习/深度学习 数据采集 算法
Python实现ISSA融合反向学习与Levy飞行策略的改进麻雀优化算法优化支持向量机分类模型(SVC算法)项目实战
Python实现ISSA融合反向学习与Levy飞行策略的改进麻雀优化算法优化支持向量机分类模型(SVC算法)项目实战
下一篇
无影云桌面