首页 > 编程语言 >快速幂算法——求a^b % p的一种快速方法

快速幂算法——求a^b % p的一种快速方法

时间:2023-04-21 20:46:44浏览次数:26  
标签:10 return temp int 快速 long 算法 ans 方法

先想暴力怎么求解

可以循环b次,每次从而求出a^b % p,时间复杂度为O(b),而这里的b是很大的,达到了2 * 10 ^ 9数量级,所以这么做会TLE

 1 #include <iostream>
 2 using namespace std;
 3 int main() {
 4     int a, b, p;
 5     long long res = 1;
 6     cin >> a >> b >> p;
 7     while(b--)
 8         res = res * a % p;
 9     cout << res << endl;
10 } 

如何优化?

我们可以用二分的思想来优化:

1) 如果b是偶数,我们可以将b / 2,求出a ^ (b / 2),然后再求平方;

2) 如果b是奇数,我们可以先将b - 1,b - 1即为偶数,记b' = b - 1,求出a ^ (b' / 2),求平方后再乘以a,仍然可以得到正确结果

3) 递归出口为a ^ 0 = 1

以上就是递归求快速幂的思路,代码如下:

 1 //递归快速幂
 2 int qmi(int a, int n)
 3 {
 4     if (n == 0)
 5         return 1;
 6     else if (n % 2 == 1)
 7         return qmi(a, n - 1) * a;
 8     else
 9     {
10         int temp = qmi(a, n / 2);
11         return temp * temp;
12     }
13 }

 

这里的temp是需要的,如果不加temp的话,直接return qmi(a, n / 2) * qmi(a, n / 2),那么会计算两次an/2,这样算法就退化成了O(n)。

在实际问题中,题目常常会要求对一个大素数取模,这是因为计算结果可能会非常巨大,但是在这里考察高精度又没有必要。这时我们的快速幂也应当进行取模,此时应当注意,原则是步步取模,如果MOD较大,还应当开long long

 1 //递归快速幂(对大素数取模)
 2 #define MOD 1000000007
 3 typedef long long ll;
 4 ll qpow(ll a, ll n)
 5 {
 6     if (n == 0)
 7         return 1;
 8     else if (n % 2 == 1)
 9         return qpow(a, n - 1) * a % MOD;
10     else
11     {
12         ll temp = qpow(a, n / 2) % MOD;
13         return temp * temp % MOD;
14     }
15 }

 递归虽然简洁,但会产生额外的空间开销。我们可以把递归改写为循环,来避免对栈空间的大量占用,也就是非递归快速幂

非递归快速幂

这里用一个例子更形象地说明,比如计算7的10次方;这里可以把10写成二进制,即(1010)2

也就是计算 7(1010)2 可以把它拆成 7(1000)2 * 7(10)2

对于任意的整数,我们都可以把它拆成二进制数的形式

那么,我们就可以每次将底数平方,从而不断地得到我们所需要的乘数因子

上代码:

 1 //非递归快速幂
 2 int qpow(int a, int n){
 3     int ans = 1;
 4     while(n){
 5         if(n&1)        //如果n的当前末位为1
 6             ans *= a;  //ans乘上当前的a
 7         a *= a;        //a自乘
 8         n >>= 1;       //n往右移一位
 9     }
10     return ans;
11 }

 

仔细推敲代码过程:

最初ans为1,然后一位一位算:

1010的最后一位是0,所以a^1这一位不要。然后1010变为101,a变为a^2。(a ^ 2)

101的最后一位是1,所以a^2这一位是需要的,乘入ans。101变为10,a再自乘。(a ^ 4)

10的最后一位是0,跳过,右移,自乘。(a ^ 8)

然后1的最后一位是1,ans再乘上a^8。循环结束,返回结果。

>>是右移,表示把二进制数往右移一位,相当于/2;&是按位与,&1可以理解为取出二进制数的最后一位,相当于%2==1。

下面给出Acwing上模板题的代码

 1 #include <bits/stdc++.h>
 2 using namespace std;
 3 #define int long long 
 4 
 5 int qmi(int a, int b, int p) {
 6     int ans = 1;
 7     while(b) {
 8         if(b & 1) ans = ans * a % p;
 9         a = a * a % p;
10         b = b >> 1;
11     }
12     return ans;
13 }
14 
15 signed main() {
16     int T;
17     cin >> T;
18     while(T--) {
19         int a, b, p;
20         cin >> a >> b >> p;
21         int res = qmi(a, b, p);
22         cout << res << '\n';
23     }
24     return 0;
25 }

 

 


 

参考博客

https://zhuanlan.zhihu.com/p/95902286

标签:10,return,temp,int,快速,long,算法,ans,方法
From: https://www.cnblogs.com/i-rong/p/17341685.html

相关文章

  • 【Spring】静态方法(工具类)中调用Spring管理的Bean
    背景在一些业务开发,经常会写一些工具类,但这些工具类时常需要调用到Spring管理的bean,这些Spring管理的bean注入,平常用的都是@Autowired注解一个成员变量,问题就来了:(1)成员变量(即Spring管理的bean)是非静态的,但工具类都是想写静态方法,静态方法不能引入一个非静态的变量。所以......
  • 基础算法-快速排序
    思路快速排序是一种常见的排序算法,它的基本思路是通过分治的方法将一个大的问题分解成小的问题进行解决。具体而言,快速排序的核心思路是选取一个枢轴元素,将序列分为两个子序列,其中一个子序列的所有元素都比枢轴元素小,而另一个子序列的所有元素都比枢轴元素大,然后对这两个子序列分......
  • 基础算法-堆排序
    思路堆是一种完全二叉树,其中每个节点的值都大于或等于其子节点的值,被称为“大根堆”;或者每个节点的值都小于或等于其子节点的值,被称为“小根堆”。在堆排序中,我们使用的是大根堆,即根节点的值是最大的元素。堆排序的基本思路是:建立一个大根堆。将待排序的序列构建成一个大根堆,......
  • 基本算法-基数排序
    思想当我们需要对一组数据进行排序时,常规的排序算法(如快速排序、归并排序等)通常是比较排序,即通过比较元素之间的大小关系来进行排序。但有时候我们需要对一组数据按照它们的“数字位”进行排序,此时比较排序并不是最优的选择,这时候基数排序就显得非常有效了。基数排序是一种非比......
  • ruoyi+EasyCode的快速开发
    https://www.cnblogs.com/SjhCode/p/EasyCode.html1.导入ruoyi模板项目若依官网:http://doc.ruoyi.vip/ruoyi/例子:https://baijiahao.baidu.com/s?id=1716723778024031543&wfr=spider&for=pc代码:gitclonehttps://gitee.com/y_project/RuoYi.git初学ruoyi可以看这位博主的视......
  • 电解电容符号及使用方法
    符号1.隔直流:作用是阻止直流通过而让交流通过。2.旁路(去耦):为交流电路中某些并联的组件提供低阻抗通路。3.耦合:作为两个电路之间的连接,允许交流信号通过并传输到下一级电路4.滤波:这个对DIY而言很重要,显卡上的电容基本都是这个作用。5.温度补偿:针对其它组件对温度的适应性不够带来......
  • 基于RL(Q-Learning)的迷宫寻路算法
    强化学习是一种机器学习方法,旨在通过智能体在与环境交互的过程中不断优化其行动策略来实现特定目标。与其他机器学习方法不同,强化学习涉及到智能体对环境的观测、选择行动并接收奖励或惩罚。因此,强化学习适用于那些需要自主决策的复杂问题,比如游戏、机器人控制、自动驾驶等。强化......
  • 状态监测与故障诊断常用的方法
     可作为机械设备状态监测与故障诊断的信息是多种多样的,主要有:振动、声音、变形、应力、裂纹、磨损、腐蚀、温度、压力、流量、电流、转速、扭矩、功率、等等。大机组状态监测与故障诊断常用的方法,主要有以下几种。1.振动分析法 振动分析法是对设备所产生的机械振动(对大机组......
  • 前沿Frontier:齿轮箱健康状态监测方法
    齿轮箱以其可靠高效平稳的调节传动比、改变传动方向的属性,在风力发电风力发电、矿山机械、船舶、汽车等诸多领域有着广泛应用。例如,风力发电过程中,采用齿轮增速箱对主传动轴进行加速,以实现高效的电磁转换;矿山机械、汽车、船舶中常采用齿轮减速箱对传动轴进行减速,以降低转速并获得......
  • Redis 为何使用Nearly LRU 算法淘汰数据
    Redis使用该LRU算法淘汰过期数据吗?不是的。由于LRU算法需要用链表管理所有的数据,会造成大量额外的空间消耗。大量的节点被访问就会带来频繁的链表节点移动操作,从而降低了Redis性能。Redis的内存空间是很宝贵的,而维护LRU的双向链表需要使用比较多的额外空间,至少需要一......