大数乘方取余
输入a,n(1 <a,n<1e9)和k(1<k<1e4)求a^n % k的余数。
我只想说...自学算法真是虐心!好不容易以为自己攻克了一个算法题,上网一查,全是错的,擦,不废话,开始分析:
经过网上的多方参考,大数乘方取余用模重复平方算法,这个算法的介绍是这样的:
由于a^n的数太大,所以直接模除不行,因此我们引进二进制的思想,比如:a^13 % k=[ a^(2^3) * a^(2^2) * a^(2^1) ] % k = [a^(2^3) %k] * [a^(2^2) %k] * [a^(2^1) %k]
这样就相当于把大数乘方取余分拆成由低位到高位的若干个小很多的数取余,因为指数n被分拆为以2为底数,因为这个算法的复杂度是logn,代码如下:
1 #include "stdafx.h" 2 #include <stdlib.h> 3 //由于输入的数可能比较大,所以定义无符号的长整型 4 unsigned long int Exponent_mod(const unsigned int a, unsigned int n,const unsigned int k) 5 { 6 unsigned long int r = 1,b = a; 7 //当输入指数为0时 8 if(n == 0) 9 { 10 return r; 11 } 12 while(n) 13 { 14 //由于是b=a是由低位到高位递增,而n/=2是高位到低位递减,所以n%2==1是判断当前项的b是否已经满足分拆之后的某一项,例如8^24 = 8^16 * 8^8(最低项是8^8,这需要8^1运算3次) 15 if(n % 2 == 1) 16 { 17 //对于每一项模除取余 18 r = (r * b) % k; 19 } 20 //每运算一次迭代递增 21 b = (b * b) % k; 22 //每次规模减少一半 23 n /= 2; 24 } 25 return r; 26 } 27 int _tmain(int argc, _TCHAR* argv[]) 28 { 29 unsigned long int a,n,k; 30 printf("请输入底数,指数,和需要模除的数:"); 31 //由于涉及到大数,所以不能用%d,细节注意一下 32 scanf("%lu,%lu,%lu",&a,&n,&k); 33 printf("此次运算的余数是:%lu\n",Exponent_mod(a,n,k)); 34 system("Pause"); 35 return 0; 36 }
这道算法题,运用到了分治法的思想,通过每一次的迭代将问题规模缩小,是分治法的基础应用,关于分治法的介绍,今后会结合算法题再次学习。
尼玛..一道基础题耗了一天...