![]() 求a^b%c(这就是著名的RSA公钥的加密方法) 当a,b很大时,直接求解这个问题不太可能 你能想到哪些优化呢? 算法1:直观上,也许最容易想到的是利用a*b%c=((a%b)*a)%c,这样每一步都进行这种处理,这就解决了a^b可能太大存不下的问题,但这个算法的时间复杂度依然是O(n),根本没有得到优化。当b很大时运行时间会很长 算法2:另一种算法利用了分治的思想,可以达到O(logn)。 可以把b按二进制展开为b=p(n)*2^n+p(n-1)*2^(n-1)+...+p(1)*2+p(0) 其中p(i) (0<=i<=n)为0或1 这样a^b=a^(p(n)*2^n+p(n-1)*2^(n-1)+...+p(1)*2+p(0)) =a^(p(n)*2^n)*a^(p(n-1)*2^(n-1))*...*a^(p(1)*2)*a^p(0) 对于p(i)=0的情况,a^p(i)*2^(i-1)=a^0=1,不用处理 我们要考虑的仅仅是p(i)=1的情况 a^(2^i)=(a^(p(i)*2(i-1)))^2 利用这一点,我们可以递推地算出所有的a^(2^i) 当然由算法1的结论,我们加上取模运算a^(2^i)%c=((a^(2(i-1))%c)*a^(2(i-1)))%c 于是再把所有满足p(i)=1的a^(2^i)%c按照算法1乘起来再%c就是结果 示例: 3^6%7=3^(2^2)*3^(2^1)%7 =((3^(2^1))^2%7)*(3^1*3^1%7) =(((3^1*3^1%7)%7)^2%7*2%7)%7 =(4*2)%7 =8%7 =1 当然算法可以进一步改进,比如二进制的每一位不必存起来,可以边求边用 经改进后代码如下:(输入a,k,m,求a^k%m) long f(long a,long k,long m) { long b=1; while(k>=1) { if(k%2==1) b=a*b%m; a=a*a%m; k=k/2; } return b; } 这种算法不仅仅局限于整数的幂取模运算,同样适用于一切满足结合律的元素的幂取模运算,例如 矩阵 看一下一道题: http://acm.pku.edu.cn/JudgeOnline/problem?id=3070 In the Fibonacci integer sequence, 0, 1, 1, 2, 3, 5, 8, 13, 21, 34, …An alternative formula for the Fibonacci sequence is .Given an integer The input test file will contain multiple test cases. Each test case consists of a single line containing n (where 0 ≤ For each test case, print the last four digits of 0 9 999999999 1000000000 -1 0 34 626 6875 As a reminder, matrix multiplication is associative, and the product of two 2 × 2 matrices is given by .Also, note that raising any 2 × 2 matrix to the 0th power gives the identity matrix: .大意是通过求矩阵[1 0]的幂来求 [1 1] 最后得到的矩阵的右上角的元素将是F(n),如果F(n)大于10000就取F(n)%10000 代码如下: #include <stdio.h> typedef struct { int a11,a12,a21,a22; }ma; void f(ma x,ma y,ma &z) { z.a11=(x.a11*y.a11%10000+x.a12*y.a21%10000)%10000; z.a12=(x.a11*y.a12%10000+x.a12*y.a22%10000)%10000; z.a21=(x.a21*y.a11%10000+x.a22*y.a21%10000)%10000; z.a22=(x.a21*y.a12%10000+x.a22*y.a22%10000)%10000; } int a[35]; int g(int n) { int i=0,len; while(n!=0) { a[i]=n%2; n=n/2; i++; } len=i; return len; } ma power(int n) { int len,i; ma s,x; ma p[35]; p[0].a11=1; p[0].a12=1; p[0].a21=1; p[0].a22=0; s.a11=1; s.a12=1; s.a21=1; s.a22=0; len=g(n); for(i=1;i<=len-1;i++) { f(p[i-1],p[i-1],p[i]); } for(i=0;i<=len-1;i++) { if(a[i]==1) { x.a11=s.a11; x.a12=s.a12; x.a21=s.a21; x.a22=s.a22; f(x,p[i],s); } } return s; } int main() { int n; ma s; while(1) { scanf("%d",&n); if(n==-1) break; if(n==0) { printf("0/n"); continue; } if(n==1 || n==2) { printf("1/n"); continue; } s=power(n-1); printf("%d/n",s.a12); } return 0; } 可以说一切常系数的线性递推序列都可以通过构造矩阵来运算,可以达到O(logn)的时间复杂度。至于怎么构造,是另一个问题了。 ![]() |
快速幂取余,常系数递推数列通项的快速求解和RSA公钥
最新推荐文章于 2021-07-27 10:31:43 发布