1:
問題如下:
求 a^n % m 的值是多少?n是1到10^18次方的一個整數。
求一個數的n次方,朴素的算法就是直接for循環,O(N)的復雜度。
但是對於這個問題n實在是太大了,O(N)也會超時,那么需要更快的算法,快速冪算法。
要求 a^n,如果知道了 a^(n/2) 次方的話,再來個平方就可以了。
那么按照這個思路就能運用分治的思想了。
代碼如下:
1 int _pow(int a,long long n,int m) { 2 if(n==0) return 1 % m; 3 4 long long t=_pow(a,n/2,m); 5 6 if(n%2==1) return (t*t*a) % m; 7 else return (t*t) % m; 8 }
如上運用分治的思想,只需要logN的復雜度就可以得到答案。
2:
問題如下:
f(1)=1, f(2)=1 , f(n)=a*f(n-1)+b*f(n-2),輸出n和m,求 f(n) % m 的值。n是1到10^18次方的數。
朴素的想法同上,直接一個for循環遞推過去,這樣復雜度是O(N)的,還是比較慢。
然后想到高中的數學問題,用特征方程求這個遞推式的非遞推通項方程,求出是 f(n)=c1*x1^n+c2*x2^n ,這樣的話應用前面的快速冪就可以求解了。
但是x1和x2大部分情況是小數,這是求出來會有誤差而且沒法取模,並不能算出精確值來。
考慮矩陣這種數學工具,構造矩陣:
則求 f(n) 的話如下:
那么只要用快速冪求出矩陣的n-2次方來,因為都是整數,所以不會有精度問題,也就得到了正確答案。
也就是矩陣快速冪。
3,快速乘:
問題:
求 (a*b) % m 的值,其中 a,b,m 是1到10^18。
如果直接乘的話,因為a和b還有m都很大,那么會溢出long long,所以需要一些方法。
朴素的想法是用數組模擬高精度,但是比較麻煩。
還有更好的方法:
求乘法的列豎式,
1234*213=1234*3+1234*10*1+1234*10^2*2;
那么如果變成二進制的話 10101 × 1011 = 10101*1+10101*2^1*1+10101*2^2*0+10101*2^3*1;
這樣代碼如下:
1 long long multi(long long a,long long b,long long m) { 2 long long ans=0; 3 4 while(b) { 5 if(b&1) (ans+=a) %= m; 6 (a=a*2) %= m; 7 b/=2; 8 } 9 10 return ans; 11 }
就是模擬了二進制的豎式乘法,因為每次最多×2,所以不會溢出。
這樣的復雜度是 logN 的。