`

高阶幂的求余的方法

 
阅读更多

通常会有如下问法:

 

    有两个数,A和B,A的范围较小,B的范围较大。问A的B次幂的最后n位是多少? n一般都小于5.

 

    有两个数,A和B,他们的范围都很大。问A的B次幂的最后n位是多少?

 

 

该题目主要思路是A的B次幂最后求余即可。但是A和B要不一个很大,要不都很大,是很难求到的。

 

都用到了数学里的同余数的概念。

 

同余数概念如下:

 

采用同余的习惯写法,对三个整数a,b,p, p>0
  a=b (mod p) (其实等式应该是三横,我没有找到HTML的表示方法)
表示a%p=b%p. 同余有个很好的基本性质: 支持加法和乘法. 即:
若 a=b (mod p), c=d (mod p), 则有
  a+c=b+d (mod p), a*c=b*d (mod p)
因此, 若a=b(mod p), 则对任意自然数n, 有
  a^n=b^n (mod p)
上式可以帮助解决楼主的问题(求a^n除以p的余数): 若a是个很大的数,我们先求出它除以p的余数b,然后计算b^n除以p的余数就行了.
但是,如果n也很大, 计算b^n的工作量还是不小. 受启于同余式中的Euler定理, 我们可有如下作法:
考虑b,b^2,b^3,...b^n,...这些数除以p的余数得到的数列S,S(有无穷项)中每一项都是0到p-1之间的某一个数.因此,一定有不相等的两个数m,n使得
  S[m]=S[n]
注意只要S[m]=S[n], 就一定有S[m+1]=S[n+1], S[m+2]=S[n+2], ... s[m+k]=S[n+k], ...
因此, 数列S实际上是循环的,并且循环的周期小于等于p, 因此用程序可很快算出其周期.

 

----------------------------------------------------------------------

 

 

取模的方法有一点a^(2^n)%c=(a^2%c)^(2^(n-1))%c

#include <iostream> using namespace std; long PowerMod(long a, long b, int k) { long tmp = a, ret = 1; while (b) { if (b & 1) ret = ret * tmp % k; tmp = tmp * tmp % k; b >>= 1; } return ret; } int main() { long a; long b; int i,N; cin>>N; for(i=0;i<N;i++) { cin>>a>>b; cout<<PowerMod(a,b,9907)<<endl; } return 0; }

 

 


 

求最后几位,如最后四位,只要模10000就行了。

要算一共有多少位,可以求log10(a^b)=blog10(a)(小数进位),例如5^10

有10*log10(5)=6.98,进位得7。

 

 

 

【解释】

 

zz: http://blog.csdn.net/shuqin1984/article/details/6543677

 

整数的二进制分解

 将大整数 power 按照二进制进行分解:

            power = b[N] * 2^N + b[N-1] * 2^(N-1) + … + b[1] * 2 + b[0]

其中: b[i] 取值为 0 或 1 ( i=0,1,.., N),则

           a^power = a^(b[N] * 2^N) * a^(b[N-1] * 2^(N-1)) * … * a^(b[1] * 2) * a^b[0]
 很显然:  

          (1) 若 b[i] = 0, 则 b[i] * 2^i = 0 , a^(b[i]*2^i) = 1, 该乘积项可以消去;

          (2) 若 a[i] = 1, 则 b[i] * 2^i = 2^i , a^(2^i) % m = (a^(2^(i-1)) % m)^2 % m.  

                令 temp = a^(2^(i-1)) % m , 则 a^(2^i) % m = (temp * temp) % m。

比如,  power = 26 = 16 + 8 + 2 = (11010)2, 则 a^26 = a^(2^4 + 2^3 + 2^1);  

计算 a^power 实际上是计算 power 的二进制表示中所有位为1对应的乘积项。

                (a^26) % m = ((a^16 %m) * (a^8 %m) * (a^2 % m)) %m

而, a^8 % m = ((a^4 %m) × (a^4%m)) % m  是可以用动态规划法逐次求解的。 简单起见, 将 (a^i) % m 称为 “取模乘积项”。

 

 

 

算法描述:

令 temp = a^(2^i) % m , i 是 power 的当前二进制位所对应的位置,

                                        temp 表示 power 的当前二进制位所对应的(取模)乘积项

STEP1:   初始化 temp 为 a % m  ,  result = 1;

STEP2:   对 power 进 行二进制分解。 若 power >=1 , 则进行模2运算:否则转至 STEP3

 [1]  若余数为1, 则该位置上的二进制为1, 乘积中需要加入此时的 temp 项 :  result = (result * temp) % m; 

           下一个二进制位对应的乘积项为 temp = (temp * temp) % m 

 [2]  若余数为0, 则该位置上的二进制为0,乘积中不需要加入 temp 项, result 保持不变, 

           下一个二进制位对应的乘积项为 temp = (temp * temp) % m 

STEP3: 所有的二进制位对应的乘积项都已计算,算法结束。

 

 

 

比如, result = (3^26) % 5 的计算过程如下:26 = (11010)2 ; 

(1)初始化:  temp = 3^1 % 5 = 3;, result = 1 ;  

 

(2)   检测 26 的最低位二进制为0,  则 不加入乘积项,result = 1,   temp =(3^2) % 5 = (temp * temp) % 5 = 4

 

 (3)   检测 26 的次低位二进制为1, 则 加入乘积项, result = (result * temp) % 3 = 4 , temp = (3^4) % 5 = (4*4) % 5 = 1;

 

 (4)   检测 26 的次低位二进制为0, 则 不加入乘积项, result = 4, temp = (3^8) % 5 = (1*1) % 5 = 1; 

  (5)   检测 26 的次低位二进制为1, 则 加入乘积项, result = (result * temp) % 5 = 4, temp = (3^16) % 5 = 1;

 

 (6)   检测 26 的次低位二进制为1, 则 加入乘积项, result = (result * temp) % 5 = 4, temp = (3^32) % 5 = 1.

 

由于所有位都检测完毕, 故 3^26 % 5 = 4.  由上可知, 

 

3^26 % 5 = ((3^16) % 5)) * ((3^8) % 5) * ((3^2) % 5) % 5.  其中 3^16 % 5, 3^8 % 5, 3^2 % 5 是通过动态规划法逐渐求得的。

 

  1. /* 
  2.  * bintmode.c :  此程序计算 (a^power) % mod. power 是大整数 
  3.  * 基本公式: (a*b) mod m = ((a mod m) * (b mod m)) mod m 
  4.  */  
  5.   
  6. #include <stdio.h>  
  7. #include <assert.h>  
  8.   
  9. int modMRec(intintint);  
  10. int modMDyn(intint ,int);  
  11. void testModMRec(int);  
  12. void testModMDyn(int);  
  13.   
  14. void testValid(int (*fun)(int a, int power, int mode));  
  15. void testInvalid(int (*fun)(int a, int power, int mode));  
  16.   
  17. int main()  
  18. {  
  19.    printf(" ******** 使用递归技术求解: ******** \n");  
  20.    testValid(modMRec);  
  21.    /* testInvalid(modMRec);  */   
  22.    defRuntime(testModMRec);  
  23.   
  24.    printf(" ******** 使用二进制分解技术求解: ******** \n");  
  25.    testValid(modMDyn);  
  26.    /* testInvalid(modMDyn);  */   
  27.    defRuntime(testModMDyn);  
  28.   
  29.    return 0;  
  30. }  
  31.   
  32. /* 
  33.  * modMRec: 递归求解 (a^power) % mod , power 是个大整数 
  34.  */  
  35. int modMRec(int a, int power, int mod)  
  36. {  
  37.    assert(a>=1 && power >=0 && mod >=1);  
  38.    if (power == 0) {  
  39.        return 1 % mod;  
  40.    }  
  41.    if (power == 1) {  
  42.        return a % mod;  
  43.    }  
  44.    if (power % 2 != 0) {  
  45.        int temp = modMRec(a, power/2, mod);  
  46.        return  (temp * temp * a) % mod;  
  47.    }   
  48.    else {  
  49.        int temp = modMRec(a, power/2, mod);  
  50.        return  (temp * temp) % mod;  
  51.    }  
  52. }  
  53.   
  54. /* 
  55.  * modMDyn: 使用二进制分解技术求解 (a^power) % mod , power 是个大整数 
  56.  */  
  57. int modMDyn(int a, int power, int mod)  
  58. {  
  59.    assert(a>=1 && power >=0 && mod >=1);  
  60.    int result = 1;  
  61.    int temp = a % mod;  
  62.    while (power >= 1) {  
  63.       if (power % 2 != 0) {  
  64.          result = (result * temp) % mod;     
  65.       }  
  66.       temp = (temp * temp) % mod;  
  67.       power /= 2;     
  68.    }  
  69.    return result;  
  70. }  
  71.   
  72. void testValid(int (*fun)(int a, int power, int mode))  
  73. {  
  74.    int base[5] = {2,3,5,7,11};  
  75.    int i,k;  
  76.   
  77.    for (i=0; i < 5; i++) {  
  78.      for (k = 0; k < 20; k++) {  
  79.         printf("%d ^ %d mod %d = %d .\n", base[i], k, 10, (*fun)(base[i], k, 10));  
  80.      }      
  81.    }  
  82. }  
  83.   
  84. void testInvalid(int (*fun)(int a, int power, int mode))  
  85. {   
  86.    printf("0 ^ 3 mod 5");  
  87.    (*fun)(0, 3, 5);    
  88.    printf("3 ^ -1 mod 5");  
  89.    (*fun)(3, -1, 5);  
  90.    printf("3 ^ 5 mod 0");  
  91.    (*fun)(3, 5, 0);  
  92. }  
  93.   
  94. void testModMRec(int n)  
  95. {  
  96.    modMRec(2, n, 10);  
  97. }    
  98.   
  99. void testModMDyn(int n)  
  100. {  
  101.    modMDyn(2, n , 10);  
  102. }  

 

  1. #include <stdio.h>  
  2. #include <time.h>  
  3.   
  4. #define MAX_SCALE 10  
  5.   
  6. long defScale[MAX_SCALE] = {1, 10, 100, 1000, 10000, 100000, 1000000, 10000000, 100000000, 1000000000};  
  7.   
  8. /* runtime: 测量 fun 指向的函数的运行时间 
  9.  * fun: 指向测试函数的指针 ; 
  10.  * scale:  问题规模数组  
  11.  */  
  12. void runtime(void (*fun)(long scale), long* scale, int len);  
  13.   
  14. void defRuntime(void (*fun)(long scale));  
  15.   
  16. void runtime(void (*fun)(long scale), long *scale, int len)  
  17. {  
  18.    int i;  
  19.    clock_t start, end;  
  20.    for (i = 0; i < len; i++) {  
  21.       start = clock();  
  22.       (*fun)(scale[i]);  
  23.       end = clock();  
  24.       printf("scale: %12ld\t run time: %8.4f (ms). \n", scale[i], ((double)(end-start)) * 1000 / CLOCKS_PER_SEC);  
  25.    }  
  26. }  
  27.   
  28. void defRuntime(void (*fun)(long scale))  
  29. {  
  30.    runtime(fun, defScale, MAX_SCALE);  
  31. }  

 

 


 

分享到:
评论

相关推荐

    幻方构造方法

    此外,文章中还提到了幻方的变换、构造完美幻方的方法、构成高次幂幻方的简捷方法以及多维幻方的通解公式。这些内容为我们提供了深入研究幻方构造的多种途径和方法。 通过这些构造方法的学习和掌握,可以帮助我们更...

    高等数学公式导数公式:基本积分表:三角函数的有理式积分:一些初等函数: 两个重要...高阶导数公式——莱布尼兹(Leibniz)公式:中值定理与导数应用:曲率

    高阶导数公式——莱布尼兹(Leibniz)公式:中值定理与导数应用:曲率" 高等数学是数学的一个重要分支,涉及到微积分、矢量分析、函数论等多个方面。以下是从给定的文件中提取的知识点: 1. 高等数学公式导数公式...

    泰勒公式及其在在计算方法中的应用讲解.pdf

    泰勒公式的基本思想是将函数在某一点附近的局部行为通过幂级数来近似,这对于理解和解决高阶微积分问题至关重要。 泰勒公式有两种常见的形式,一种带有拉格朗日型余项,另一种带有佩亚诺型余项。拉格朗日型余项Rn(x...

    泰勒级数把函数展开成幂级数

    2. 拉格朗日余项:泰勒级数通常不能完全展开一个函数,因此存在一个余项,拉格朗日余项用来描述未被级数包含的部分,其形式为 \( R_n = \frac{f^{(n+1)}(\xi)}{(n+1)!}(x-a)^{n+1} \),其中 \( \xi \) 在a与x之间。 ...

    行列式的计算方法~计算行列式的格式.doc

    为了应对高阶行列式的计算,文章介绍了几种常用方法: 1. **化为三角形法**:通过行或列的初等变换,将行列式转化为上三角形或下三角形,这样行列式的值就是主对角线上元素的乘积。如果非零元素位于副对角线,那么...

    正切函数的幂级数表示与Genocchi数的恒等式 (2014年)

    利用初等方法研究了正切函数的幂级数表示与Genocchi数及高阶Genocchi...通过正切函数的幂级数展开以及比较系数的方法,揭示了其系数与Genocchi数及高阶Genocchi数的关系,并由此得到了关于Genocchi数的几个有趣的同余式.

    泰勒公式及其在在计算方法中的应用讲解.docx

    这个公式能够将函数展开成幂级数,从而简化计算过程,尤其是在处理高阶导数和极限问题时。泰勒公式的形式通常包括一个主项和一个余项,余项用来描述近似值与真实值之间的偏差。 泰勒公式的基本形式是: \[ f(x) = ...

    数值计算课后答案解析4.doc

    7. 高阶插值与余项分析:对于有较高阶导数的函数,可以构造更低次数的插值多项式。本题中,构建了满足特定条件的三次埃尔米特插值多项式,并通过分析余项的结构,展示了余项表达式的推导过程。 这些解答涵盖了数值...

    泰勒公式及其在在计算方法中的应用.docx

    公式中的n阶导数反映了函数在该点附近的信息,n越大,展开的精度越高。 在实际应用中,泰勒公式可以用于: 1. **误差分析**:通过泰勒公式,我们可以估计近似计算中的误差大小。例如,在数值积分中,将被积函数...

    三种不同的曲线拟合方法_三种不同的函数表示方法进行曲线拟合_曲线拟合方式_

    当数据呈现非线性趋势时,可以使用更高阶的多项式进行拟合。多项式回归通过构建高次多项式函数来近似数据,例如二次、三次或更高次。MATLAB中的`polyfit`函数也适用于多项式拟合,只需改变第三个参数为所需的多项式...

    计算方法试卷及答案2017-41

    2. **矩阵特征值计算**:计算矩阵按模最大特征值的方法是幂法,对于按模最小特征值,使用反幂法。实对称矩阵的全部特征值可以通过Jacobi方法求得,这是一种迭代方法,用于找到矩阵的所有特征值。 3. **Lagrange插值...

    数值分析试题(库)与答案解析.doc

    综合题部分涉及到多项式拟合、求积公式的代数精度计算、Newton法求解方程根、矩阵直接三角分解、数值积分法解初值问题以及幂法求特征值等实际应用。 证明题部分通常涉及收敛性证明,例如证明迭代格式对特定函数的...

    成考专升本高等教育数学[二]重点和解析[精简版].doc

    5. **高阶导数**:了解二阶导数的表示及其求法,以及更高阶导数的概念。 **三、微分的求法** 1. 微分是导数乘以dx的结果,求微分首先要求导数。 **四、二元函数的微分学** 1. **多元函数**:理解多元函数的定义,...

    大学高数公式(积分 微积分 三角函数)

    基本的积分公式包括幂函数、指数函数、对数函数、三角函数的积分,以及积分的线性性质和换元积分法。定积分则用于计算面积、弧长、体积等,其基本定理连接了微分与积分,是微积分学的基石。 2. **微积分**:微积分...

    ACM数学模板

    - 高阶方程求根:利用数值方法求解高阶方程的根。 - 快速傅里叶变换:用于快速计算多项式的乘法和离散傅里叶变换。 8. 进制转换和格雷码:用于在不同进制之间转换数据和处理格雷码。 ACM数学模板中的每一个知识...

    nizhen.rar_矩阵求逆

    综上所述,“nizhen.rar_矩阵求逆”这个压缩包提供了一种用C语言实现的矩阵求逆方法,利用代数余子式,这对于理解矩阵运算和编程实践具有很高的价值。通过学习和理解这段代码,开发者可以更好地掌握矩阵理论及其在...

    高数微积分公式大全

    三、高阶导数的运算法则则涉及到更高阶的导数计算,例如二阶导数、三阶导数等,对于某些特定函数,这些高阶导数有着简洁的表达式。 四、基本初等函数的n阶导数公式提供了求任意阶导数的方法,这对于分析函数的性质...

    3-3高等数学同济大学第六版本.pdf

    拉格朗日型余项则描述了麦克劳林公式中未被包含的高阶导数对函数的影响。例如,对于函数f(x) = tan x,要求求出带有拉格朗日型余项的3阶麦克劳林公式。这里需要计算tan x在x=0处的导数值,并根据麦克劳林公式构造3阶...

    《 法兰西数学精品译丛:解析函数论初步》作者: H.嘉当 译者: 余家荣 出版年: 2008年

    3 高阶微分方程 习题 一些习题的答案 名词索引 记号索引 丛书信息  法兰西数学精品译丛 (共17册), 这套丛书还有 《线性与非线性泛函分析及其应用(上)/法兰西数学精品译丛》,《拓扑学教程》,《拟微分算子和Nash-...

Global site tag (gtag.js) - Google Analytics