这次博客的话,我就来讲个比较……怎么说呢,经典的数论问题——快速幂吧。(事实上是我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中是很实用的,当然也得看好题,然后决定使用什么算法