快速幂的运算(简单易懂)

这次博客的话,我就来讲个比较……怎么说呢,经典的数论问题——快速幂吧。(事实上是我A了13年NOIP的转圈游戏)

平常我们计算n^k怎么计算呢,相信大家会经常使用下面的代码

1 int x = n;
2 for(int i = 1;i <= k; ++i){
3      x = x*n;  
4 }

那么x就是我们想要的结果,不难看出这个方法就是O(n)的算法,大家都会感觉这个复杂度很好了,但是就偏偏有那么一种题目(1≤k≤10^9甚至更加坑爹),非得卡这个时间复杂度让你超时,这样的话怎么办呢?

这时候我们先来回顾一下我们初中的数学知识:关于幂的运算

a^n = (a^b)^c(b*c = n);        a^m(m = n+d)=a^n * a^d;

这是两个初中学的幂运算的知识,然后就有一群人根据这个还有自然数的一个性质(自然数只分为奇数和偶数,也就是一个自然数可以表示成2*n或者2*n+1,n为自然数)就搞出来了一种高效的而且简单易懂的算法——快速幂

对于计算n^k的问题,通常人们不会那么变态,让你直接算出来(那怎么着都最起码要高精度了),出题人通常会让我们对于结果取一个模,然后输出模后的结果。这样的话,我们的快速幂的思想就是在下面给出:

对于求解n^k mod m的问题(不知道mod的请回去看小学除法中的余数问题),我们可以这样来想:

k要么是奇数,要么是偶数。那么我们就用到数学上的分类讨论思想了:

首先我们需要一个tot计数,我们为了求MOD,就直接在运算中求就可以了(tot初始值为1)

(1)如果k是偶数,那么n直接平方然后让n对m取模(n*=n %m),用k除以2去替代k的值;

(2)如果k是奇数,那么这时候tot要乘上n(因为这时候k=2p+1,p∈N,n^k=n^2p*n)再对m取模,n平方后再对m取模,最后也是用 k除以2向下取整 去替代k的值;

按照上两步递归或者迭代求解直至k=0的时候结束,这时tot变量里面的值便是题目要求的值。

大家可以看一下,这个快速幂的算法其实是比朴素算法更加高效的,它的时间复杂度为O(logk)级别(请大家自己算一下,是不是这个时间复杂度)。

上面就是快速幂算法的思想,其实它是一个很简单的数论问题。

不多说了,还是老话:

…………………………………代码最重要…………………………………………

(实际上是因为大多数人都是看代码去理解的,其实我并不建议这样,应该先去看思想,但是没办法,只能说是附上代码,我的代码只有递归的,迭代的代码还是请大家自己去写)

 1 #include <iostream>
 2 #include <cstdio>
 3 #include <algorithm>
 4 #include <cstring>
 5 #include <cmath>
 6 using namespace std;
 7 long long n,m,k,ans,tot = 1;
 8 void ksm(long long a,long long b,long long c){
 9     long long d,e;
10     d = b % 2;
11     e = b / 2;
12     if(b == 0)return;
13     if(d == 0){
14         a = a * a % c;
15         ksm(a,e,c);
16     }
17     if(d == 1){
18         tot = tot * a % c;
19         a = a * a % c;
20         ksm(a,e,c);
21     }
22 }
23 int main(){
24     scanf("%d%d%d", &n, &m, &k);
25     ksm(n,m,k);
26     cout << tot;
27     return 0;
28 }
View Code

以上就是快速幂算法的代码,当然也可以写int返回值的函数,大家可以自己写一下

另外想要NOIP2013提高组 day1 T1 的题解的,我把代码放在地板下(但是我个人建议还是先自己做,实际上那只是一道快速幂的水题,先不要看我的题解,你最起码得AC 4 个点然后或者是不会的时候再看,否则一点收获都没有,我的代码可是纯自己手动写的):

 1 #include <iostream>
 2 #include <cstdio>
 3 #include <algorithm>
 4 #include <cstring>
 5 #include <cmath>
 6 using namespace std;
 7 long long n,m,k,x,ans,tot = 1;
 8 void ksm(long long a,long long b,long long c){
 9     long long d,e;
10     d = c % 2;
11     e = c / 2;
12     if(c == 0)return;
13     if(d == 0){
14         b = b * b % a;
15         ksm(a,b,e);
16     }
17     if(d == 1){
18         tot = tot * b % a;
19         b = b * b % a;
20         ksm(a,b,e);
21     }
22 }
23 int main(){
24     scanf("%d%d%d%d", &n, &m, &k, &x);
25     ksm(n,10,k);
26     x = (x + m * tot) % n;
27     cout << x;
28     return 0;
29 }
View Code

这次的关于快速幂的讲解就到这里吧,希望大家认真看,这个算法在NOIP中是很实用的,当然也得看好题,然后决定使用什么算法

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值