素数筛法(埃式筛、欧拉筛、区间筛)、素数判断法(朴素、模6、Miller-Rabin及改进)、数的分解(朴素、Pollard-rho)
素数筛法
埃式筛法
O ( n l o g l o g n ) O(nloglogn) O ( n l o g l o g n )
1 2 3 4 5 6 7 8 9 10 11 12 bool b[N];bool prime[N];void solve (int x) { for (int i = 0 ; i <= x; i++) prime[i] = true ; b[0 ] = b[1 ] = 0 ; for (int i = 2 ; i <= x; i++) if (b[i]) for (int j = i << 1 ; j <= x; j += i) prime[j] = false ; }
欧拉筛法
O ( n ) O(n) O ( n )
过程:以check[i]=0
记i
为素数。筛i i i 时,已知质数prime[0...tot]
,则i × p r i m e [ j ] i\times prime[j] i × p r i m e [ j ] 一定不是质数,如果超出了要求的范围,或p r i m e [ j ] ∣ i prime[j]|i p r i m e [ j ] ∣ i 时j j j 不再增加。
停止条件的原因:对于p r i m e [ j ] ∣ i , i = p r i m e [ j ] × k prime[j]|i,i=prime[j]\times k p r i m e [ j ] ∣ i , i = p r i m e [ j ] × k ,未来一定会筛到x = p r i m e [ j + 1 ] × i = p r i m e [ j ] × k × p r i m e [ j + 1 ] x=prime[j+1]\times i=prime[j] \times k\times prime[j+1] x = p r i m e [ j + 1 ] × i = p r i m e [ j ] × k × p r i m e [ j + 1 ] 。
未来i i i 一定会增到i ′ = k × p r i m e [ j + 1 ] > k × p r i m e [ j ] = i i'=k\times prime[j+1]>k\times prime[j]=i i ′ = k × p r i m e [ j + 1 ] > k × p r i m e [ j ] = i ,这一轮一定会筛到i × p r i m e [ j + 1 ] i\times prime[j+1] i × p r i m e [ j + 1 ] 筛去。
跳出循环时p r i m e [ j ] prime[j] p r i m e [ j ] 正是p r i m e [ j ] × i prime[j]\times i p r i m e [ j ] × i 的最小质因子。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 const int MAX_L = (1e7 ) + 2 ;int a, b;bool check[MAX_L]; int prime[MAX_L];int tot = 0 ;void sieve (int x) { for (int i = 2 ; i <= x; i++) { if (!check[i]) prime[tot++] = i; for (int j = 0 ; j < tot; j++) { if (prime[j] * i > x) break ; check[prime[j] * i] = true ; if (i % prime[j] == 0 ) break ; } } }
区间筛素数
[ a , b ) [a,b) [ a , b ) ,小数区间推大数区间
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 typedef long long ll;const int MAX_L = (1e8 ) + 2 ;const int MAX_SQRTB = (1e4 ) + 2 ;ll a, b; bool isprime[MAX_L]; bool isprime_small[MAX_SQRTB]; void segment_sieve (ll x, ll y) { for (ll i = 0 ; (ll)i * i < y; i++) isprime_small[i] = true ; for (ll i = 0 ; i < y - x; i++) isprime[i] = true ; for (int i = 2 ; (ll)i * i < y; i++) if (isprime_small[i]) { for (ll j = 2 * i; (ll)j * j < y; j += i) isprime_small[j] = false ; for (ll j = max ((ll)2 , (x + i - 1 ) / i) * i; j < y; j += i) isprime[j - a] = false ; } }
素数判断法
朴素判断法
1 2 3 for (int i = 2 ; i <= sqrt (n); i++) if (x % i == 0 ) return false ;
模6判断法
1 2 3 4 5 6 7 8 9 10 11 12 bool num[5 ] = {0 , 0 , 1 , 1 , 0 };bool prime (int x) { if (x <= 4 ) return num[x]; if (x % 6 != 1 && x % 6 != 5 ) return false ; for (int i = 5 ; i * i <= x; i += 6 ) if (x % i == 0 || x % (i + 2 ) == 0 ) return false ; return true ; }
Miller-Rabin判断法
由费马小定理,n 是素数 , p ∤ a ⇒ a n − 1 ≡ 1 ( m o d n ) n是素数,p\nmid a \Rightarrow a^{n-1}\equiv 1(\mod n) n 是 素 数 , p ∤ a ⇒ a n − 1 ≡ 1 ( m o d n ) ,反过来不一定。但是概率小,重复操作约30次差不多能保证判断正确。复杂度O ( log n ) O(\log n) O ( log n ) 。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 bool Miller_Rabin (int x) { if (x < 2 ) return false ; if (x == 2 ) return true ; if ((x & 1 ) == 0 ) return false ; for (int i = 1 ; i <= 30 ; i++) { int base = rand () % (x - 1 ) + 1 ; if (qpow (base, x - 1 , x) != 1 ) return false ; } return true ; }
改进Miller-Rabin判断法
二次探测定理:如果 p p p 是一个素数, 且 0 < x < p 0<x<p 0 < x < p , 则方程 x 2 % p = 1 x^{2} \% p=1 x 2 % p = 1 的解为 x = 1 x=1 x = 1 或 x = p − 1 x=p-1 x = p − 1
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 bool check (ll a, ll n, ll x, ll t) { ll ret = qpow (a, x, n); ll last = ret; for (int i = 1 ; i <= t; i++) { ret = multi_add (ret, ret, n); if (ret == 1 && last != 1 && last != n - 1 ) return true ; last = ret; } if (ret != 1 ) return true ; return false ; } bool Miller_Rabin (ll n) { if (n < 2 ) return false ; if (n == 2 ) return true ; if ((n & 1 ) == 0 ) return false ; ll x = n - 1 , t = 0 ; while ((x & 1 ) == 0 ) x >>= 1 , t++; for (int i = 0 ; i < 8 ; i++) if (check (rand () % (n - 1 ) + 1 , n, x, t)) return false ; return true ; }
数的分解
普通整数分解
枚举因子试除
素数打表试除(kuangbin的模板)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 ll factor[100 ][2 ]; int fatCnt;int getFactors (ll x) { fatCnt = 0 ; ll tmp = x; for (int i = 1 ; prime[i] <= tmp / prime[i]; i++) { factor[fatCnt][1 ] = 0 ; if (tmp % prime[i] == 0 ) { factor[fatCnt][0 ] = prime[i]; while (tmp % prime[i] == 0 ) { factor[fatCnt][1 ]++; tmp /= prime[i]; } fatCnt++; } } if (tmp != 1 ) { factor[fatCnt][0 ] = tmp; factor[fatCnt++][1 ] = 1 ; } return fatCnt; }
大整数分解:Pollard-rho
本质:随机因子检查
Pollard的伪随机数生成器:f ( x ) = ( x 2 + c ) m o d N f(x)=(x^2+c)\mod N f ( x ) = ( x 2 + c ) m o d N ,随机数序列:{ x , f ( x ) , f ( f ( x ) ) ⋯ } \{x,f(x),f(f(x))\cdots\} { x , f ( x ) , f ( f ( x ) ) ⋯ } 。可以修改参数打表画图得到,这个序列会进入循环,常类似ρ \rho ρ 型,即经过一些数才进入循环
(这是kuangbin的模板)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 ll factor[100 ]; int tol; ll pollard_rho (ll x, ll c) { ll i = 1 , k = 2 ; srand (time (NULL )); ll x0 = rand () % (x - 1 ) + 1 ; ll y = x0; while (1 ) { i++; x0 = (multi_add (x0, x0, x) + c) % x; ll d = __gcd(y - x0, x); if (d != 1 && d != x) return d; if (y == x0) return x; if (i == k) { y = x0; k += k; } } } void findfac (ll n, int k) { if (n == 1 ) return ; if (Miller_Rabin (n)) { factor[tol++] = n; return ; } ll p = n; int c = k; while (p >= n) p = pollard_rho (p, c--); findfac (p, k); findfac (n / p, k); }