基础
概念
整除:如果整数 b b b可以表示成整数 a a a的整数 k k k倍,即 b = k a ( a , b , c ∈ Z ) b=ka(a,b,c\in Z) b=ka(a,b,c∈Z),则称 b b b被 a a a整除或 a a a整除 b b b,记作 a ∣ b a|b a∣b
最大公约数:若 a , b ∈ N ∗ a,b\in N^* a,b∈N∗ ,则 ( a , b ) (a,b) (a,b)表示最大的正整数 d d d,使得 d ∣ a ∧ d ∣ b d|a \land d|b d∣a∧d∣b,成为 a , b a,b a,b的最大公约数。特别的,规定任意正整数 a a a,有 ( a , 0 ) = a (a,0)=a (a,0)=a
素数
概念:除了 1 1 1与其本身外,无其它约数的正整数。特别地,1既不是素数也不是合数。
判定
判定正整数 n ( n > 1 ) n(n>1) n(n>1)是否为素数
-
枚举法
从 2 2 2枚举至 n − 1 n-1 n−1, 若其中存在数能整除 n n n,则 n n n为合数,否则 n n n为素数。复杂度 O ( n ) \Omicron(n) O(n)
小优化:若 d ∣ n d|n d∣n,则必有 n d ∣ n \frac {n}{d}|n dn∣n,因此 n n n的约数成对存在,且乘积为 n n n。任选一对约数 d 1 ⋅ d 2 = n d1\cdot d2=n d1⋅d2=n,若 d 1 > n ∧ d 2 > n d1> \sqrt n \land d2> \sqrt n d1>n∧d2>n,则 d 1 ⋅ d 2 > n d1\cdot d2>n d1⋅d2>n与 d 1 ⋅ d 2 = n d1\cdot d2=n d1⋅d2=n相矛盾,因此 n n n的任意一对约数 d 1 ≤ d 2 d1\leq d2 d1≤d2,必有 d 1 ≤ n d1\leq \sqrt n d1≤n. 又由于约数成对存在,因此我们仅需枚举 2 2 2至 n \sqrt n n判断是否存在 n n n的约数即可。复杂度 O ( n ) O(\sqrt n) O(n)
-
素数测试
费马素数测试
费马小定理:对于任意素数 p p p,若 p ∤ a p\not|a p∣a,则有 a p − 1 ≡ 1 ( m o d p ) a^{p-1} \equiv 1(mod \ p) ap−1≡1(mod p)
容易想到可以通过选取随机数检验费马小定理来判定 n n n是否是素数。但费马小定理逆定理并不成立,即使 a p − 1 ≡ 1 ( m o d p ) a^{p-1}\equiv 1(mod\ p) ap−1≡1(mod p), p p p也未必是素数,我们将满足该条件的合数称为伪素数或卡迈尔数
Miller-Rabin 素数测试
二次探测定理:若 p p p为奇素数,则 x 2 ≡ 1 ( m o d p ) x^2\equiv 1(mod\ p) x2≡1(mod p)解为 x ≡ 1 x\equiv 1 x≡1或 x ≡ p − 1 x\equiv p-1 x≡p−1.移项因式分解易证。
费马小定理结合二次探测定理,对于 n − 1 n-1 n−1,我们先分解成 n − 1 = n 0 ⋅ 2 t n-1=n_0 \cdot 2^t n−1=n0⋅2t,然后先求出 c = p n 0 c=p^{n_0} c=pn0,然后对 c c c进行 t t t次自平方操作,若出现非平凡平方根(即 1 1 1或 n − 1 n-1 n−1之外的数),则不是素数,否则通过测试
参考代码:
inline bool Miller_Rabin(ll x) {
if (x<3 || x%2==0) return (x==2);
ll a=x-1, b=0;
while (a%2==0) a>>=1, ++b;
for (R ll i=1; i<=10; i++) {
ll c=rand()%(x-1)+1;
c=quick_pow(c, a, x);
if (c==1) continue;
ll j;
for (j=0; j<b; j++) {
if (c==x-1) break;
c=(el)c*c%x;
}
if (j>=b) return false;
}
return true;
}
筛法
-
Eratosthenes筛(埃筛)
目的是筛出 2 − n 2-n 2−n内所有素数。素数即除 1 1 1与其本身外没有其它约数的正整数。因此我们可以通过约数否决掉一个数是素数的可能性。每遍历到一个素数,枚举它的整数倍,将其倍数全部排除.复杂度 O ( n l o g l o g n ) \Omicron(nloglogn) O(nloglogn)
int prime[N], cnt; bool book[N]; inline void get_prime(int n) { for (int i=2; i<=n; i++) { if (book[i]) continue; prime[++cnt]=i; for (int j=i; j<=n; j+=i) book[j]=true; } }
-
欧拉筛
如果每个合数都只标记一次,那复杂度就是线性的了。详见 O I − w i k i OI-wiki OI−wiki代码解释.复杂度 O ( n ) \Omicron(n) O(n)
```cpp
inline void euler(){
for(R ll i=2;i<=n;i++){
if(!v[i]){
v[i]=i;
prime[++sum]=i;
}
for(R ll j=1;j<=sum&&prime[j]<=v[i]&&prime[j]*i<=n;j++){
v[prime[j]*i]=prime[j];
if (i%prime[j]==0) break;
}
}
}