算法--分治--a^b%m
程序员文章站
2022-05-23 10:41:53
...
杭电上有一道十分让初学者十分蛋疼的题 a^b%m,看似很简单,但题目要求b的范围是(0,1000000000],a是32位整数范围,m是小于40000的整数。咋一看这题,貌似要用高精度。但是赤裸裸的用高精度的话,在空间复杂度以及时间复杂度上都是伤不起的!!
让我们来换个思路,有一定数学基础的人都知道,(a*b)%m 是等价于 a%m * b%m的,这样好了,可以不用高精度了,但是完全模拟的话,在规定时间内是不可能出解的。
我们再想想,是否可以减小计算量呢?有了!我们可以将 a^b 分解成为 t1*a^1+t2*a^2+t3*a^3+.....(ti=0 或者 1),我们预处理下,算出a^2i,然后将b变为二进制,乘以相应的项,我们就得到了最后的结果。运算次数大大减少了,这样是可以在规定时间内出解的。
如果我们觉得上面的方法太麻烦的话,下面的递归代码就十分简单了。
这其实是利用了一个分治的思想,要想求a^b1,先求出a^b2(其中b2=b1/2),而要求a^b2,又得求出a^b3(其中b3=b2/2)..........直到bi =1。整个问题的时间复杂度就变得非常低了。最多几十次基本操作就能得到结果(有木有感觉很神奇)。其实我们还可以将这个算法求斐波拉契数列的第10000000项对m取余,这样用到矩阵的乘法,下面给出具体的矩阵推导以及代码。
分治就是分而治之的缩写,分治的思想并不局限于这道题,它能极大的提高算法的效率。所以掌握它是十分必要的。
让我们来换个思路,有一定数学基础的人都知道,(a*b)%m 是等价于 a%m * b%m的,这样好了,可以不用高精度了,但是完全模拟的话,在规定时间内是不可能出解的。
我们再想想,是否可以减小计算量呢?有了!我们可以将 a^b 分解成为 t1*a^1+t2*a^2+t3*a^3+.....(ti=0 或者 1),我们预处理下,算出a^2i,然后将b变为二进制,乘以相应的项,我们就得到了最后的结果。运算次数大大减少了,这样是可以在规定时间内出解的。
如果我们觉得上面的方法太麻烦的话,下面的递归代码就十分简单了。
#include<iostream> using namespace std; int run(int a,int b,int m){ int t; if (b==1) return a%m; if (b&1){ t=run(a,b/2,m); return ((t*t%m)*a)%m; }else{ t=run(a,b/2,m); return (t*t)%m; } } int main(){ int a,b,m; cin >> a >> b >> m ; cout << run(a,b,m); system("pause"); return 0; }
这其实是利用了一个分治的思想,要想求a^b1,先求出a^b2(其中b2=b1/2),而要求a^b2,又得求出a^b3(其中b3=b2/2)..........直到bi =1。整个问题的时间复杂度就变得非常低了。最多几十次基本操作就能得到结果(有木有感觉很神奇)。其实我们还可以将这个算法求斐波拉契数列的第10000000项对m取余,这样用到矩阵的乘法,下面给出具体的矩阵推导以及代码。
#include<iostream> using namespace std; class Matrix{ public: int width,height; Matrix(){ } Matrix(int hei,int wid){ width = wid; height = hei; v = new int*[height]; for (int i=0;i<height;i++) v[i] = new int[width]; } void set(int hei,int wei){ v = new int*[height]; for (int i=0;i<height;i++) v[i] = new int[width]; } int** v; friend Matrix operator*(Matrix a,Matrix b){ Matrix res(a.height,a.width); //确保数据正确 //if (a.width!=b.height) retutn res; for (int i=0;i<a.height;i++){ for (int j=0;j<a.width;j++){ int ans = 0; for (int k=0;k<a.width;k++){ ans += a.v[i][k]*b.v[k][j]; } res.v[i][j] = ans; } } return res; }; friend Matrix operator%(Matrix a,int m){ Matrix ans(a.height,a.width); for (int i=0;i<a.height;i++) for (int j=0;j<a.width;j++) ans.v[i][j] = a.v[i][j] % m; return ans; } }; Matrix a(2,2); Matrix run(int b,int m){ Matrix t; if (b==1) return a%m ; if (b&1){ t=run(b/2,m)%m; return ((t*t)*a)%m; }else{ t=run(b/2,m)%m; return (t*t)%m; } } int main(){ int n,m; while(1){ cin >> n >> m; a.v[0][0] = 0; a.v[0][1] = 1; a.v[1][0] = 1; a.v[1][1] = 1; Matrix ans; ans = run(n,m); for (int i=0;i<ans.height;i++){ for (int j=0;j<ans.width;j++) cout << ans.v[i][j] << " "; cout << endl; } } system("pause"); return 0; }
分治就是分而治之的缩写,分治的思想并不局限于这道题,它能极大的提高算法的效率。所以掌握它是十分必要的。
上一篇: c++-STL-priority_queue(优先队列)
下一篇: 数据结构——并查集
推荐阅读
-
PCIe 4.0小金刚 技嘉B550M Aorus Pro主板图赏
-
比X570还厉害!微星MEG B550 Unify绝技:3个PCIe 4.0x4 M.2接口
-
只要599元的小雕!技嘉B550M AORUS ELITE评测:上锐龙9 5950X也没问题
-
依旧是神板!华硕TUF GAMING B460M PRO重炮手评测:可调电压破解TDP
-
为什么说B550是性价比最高的AMD主板!七彩虹B550M GAMING FROZEN评测
-
千元内13相供电!技嘉B550M AORUS PRO主板评测:这才是真正的小雕
-
自带M.2涡轮冰霜铠甲!微星MPG B550I GAMING EDGE WIFI评测
-
2498元还要啥X570!技嘉打造B550大雕神板:独家三条PCIe 4.0 M.2
-
i3-6100配什么主板好?四款适合i3-6100配备的B150M主板推荐
-
富士施乐M205b打印机怎么扫描文件?