總結:數論 素數
素數判定算法,經典的Rabin Miller測試,通過二次探測的方法,可以將其正確率上升到一個很高的高度。
$O(1)$的快速乘。
在一些卡常數而且爆long long的取余問題中用到快速乘。
樸素的快速乘是$O(logn)$的,從而添加了不必要的復雜度。
爆long long的,實質上是取余的結果,在long long運算中只要不涉及除法,那麽一直是對INF取余的結果,對答案沒有幹擾。
1 LL mul(LL a,LL b,LL mod){ 2 if(a<=(LL)(1e8) && b<=(LL)(1e8)) return a*b%mod;3 return (a*b - (LL)(a/(LD)mod*b + 1e-3)*mod + mod) % mod; 4 }
註意快速乘在很小的數字時並不太穩,所以特判一下。
註意一下Linux下rand()函數足夠大,不需要拓充。
轉入正題:Rabin測試。
我的Rabin在INT範圍內不會出錯,在LOGN LONG範圍內出錯的概率極低。
首先是寫一個check(LL x,LL P),用數字x檢驗P是否為素數。
1 bool check(LL x,LL P){ 2 LL tmp=P-1; 3 while(!(tmp&1)) tmp>>=1; 4 LL m=qpow(x,tmp,P); 5 if(m==1) return 1; 6 while(tmp<P){ 7 if(m==P-1) return 1; 8 tmp<<=1; m=mul(m,m,P); 9 } 10 return 0; 11 }
梳理一下過程
首先將P-1中所有的2全都除掉。
這時候如果tmp 為 P-1 或者 1是偽素數。(具體就用 $x^{tmp} = 1 (mod P)$判定)
這時候檢查$x^{2tmp} , x^{4tmp}, x^{8tmp} ....$是否等於 $P-1$。
如果沒有則說明P不是素數。
接下來基於這一個$O(logn)$判定素數,我們有$O(n^{1/4})$的分解質因數算法。
Rho算法。
Rho算法是基於一個定理對於一個小於n的數字x如果 $(x,n) ≠ 1$ 則有,(x,n)為n的因子。
這樣考慮怎樣高效地得到這樣的數字x。
1 LL Rdo(LL n,LL c){ 2 LL i=1,k=2,x,y,d,p; 3 x=Rand()%n; 4 y=x; 5 while(1){ 6 i++; 7 x=(mul(x,x,n)+c)%n; 8 if(y==x) return n; 9 p=Abs(x-y); 10 d=gcd(p,n); 11 if(d!=1&&d!=n) return d; 12 if(i==k){ 13 y=x; 14 k+=k; 15 } 16 } 17 } 18 19 int tot; 20 LL a[N]; 21 22 void down(LL n){ 23 if(n==1) return; 24 if(isprime(n)){ 25 a[++tot]=n; 26 return; 27 } 28 LL t=n; 29 while(t==n) t=Rdo(n,Rand()%(n-1)+1); 30 down(t); 31 down(n/t); 32 }
這樣就可以得到了分解質因數的高效方法。
註意一個數字的質因子最多有$O(logn)$個,因為質因數的乘積為n,而且質因數都大於1.
註意在隨機數據情況下,采用樸素分解質因數的效率也大概接近 $O(n^{\frac{1}{4}})$,只不過會受到空間的限制。
for(int i=1;prime[i]*(LL)prime[i]<=tmp;i++) { int cnt = 0; tim_cnt++; while(tmp % prime[i] == 0) tmp /= prime[i]; ans *= (cnt+1); }
註意是 <=tmp 而不是 <=n
給定一個整數N,求N最少可以拆成多少個完全平方數的和。
所以根據拉格朗日平方和定理,答案為1~4
答案為1,開方驗證
答案為2,根據勾股數定理一個數字為勾股數當且僅當其質因數分解中所有的$4n+3$項的指數為偶數。
答案為3,根據初等數論中的不等式 $n ≠ (8k + 7) \cdot 4^{m}$
不然答案為4,根據 拉格朗日平方和定理即可。
總結:數論 素數