质数
luckydrawbox
·
·
算法·理论
质数的判定
暴力判定
```cpp
bool is_prime(ll n){
if(n<2)
return 0;
ll k=sqrt(n);
for(ll i=2;i<=k;i++)
if(n%i==0)
return 0;
return 1;
}
```
### 随机化
$O(k\log n)$。
根据费马小定理,若 $p$ 为质数,则 $a^{p-1}=1\pmod p$,虽然反过来不一定成立,但使用多次随机的 $a$ 可以**大概**判定是否为质数,$k$ 可以自己设定一个大一点的数保证结果精确度。
但如果 $n$ 是一些**伪素数**(不是质数却拥有质数的一些特性的数)或判定一段连续的 $n$,则会有较大的出错概率,~~所以这拼的是rp~~,**谨慎使用**。
需要[快速幂](https://www.luogu.com.cn/blog/chenjunxiu/kuai-su-mi-qu-yu)。
```cpp
bool is_prime(int k,ll n){
if(n<2)
return 0;
for(int i=1;i<=k;i++)
if(qmi(rand(),n-1,n)^1)
return 0;
return 1;
}
```
### Miller_Rabin 算法
需要[龟速乘](https://www.luogu.com.cn/blog/chenjunxiu/jun-su-sheng)的快速版本及其优化的[快速幂](https://www.luogu.com.cn/blog/chenjunxiu/kuai-su-mi-qu-yu)。
#### 变量
- $\texttt{long long pri[14]}$:可用底数数组,其中 $pri_0$ 为个数。
#### 函数
- $\texttt{bool Miller\_Rabin(ll n)}$:判断 $n$ 是否是质数。
#### 代码
```cpp
//long long pri[14]={7,2,325,9375,28178,450775,9780504,1795265022ll};
long long pri[14]={12,2,3,5,7,11,13,17,19,23,29,31,37};
bool Miller_Rabin(ll n){
if(n<3||n%2==0)return n==2;
ll u=n-1,t=0;
while(!(u&1))u/=2,t++;
for(int i=1;i<=pri[0]&&pri[i]<n;i++){
ll a=pri[i],v=qmi(a,u,n),s;
if(v==1)continue;
for(s=1;s<=t;s++){
if(v==n-1)break;
v=mul(v,v,n);
}
if(s>t)return 0;
}
return 1;
}
```
## 质数的筛选
### Eratosthenes 筛法
$O(N\log\log N)$。
```cpp
bool v[N];
void Eratosthenes(int n){
memset(v,0,sizeof(v));
for(int i=2;i<=n;i++){
if(v[i])
continue;
for(int j=i;j<=n/i;j++)
v[i*j]=1;
}
}
```
若 $v_i=0$ 且 $i\ge2$,则 $i$ 是质数。
### 线性筛法
$O(N)$。
```cpp
int m;
int v[N],prime[N];
void Q_prime(int n){
m=0;
memset(v,0,sizeof(v));
for(int i=2;i<=n;i++){
if(!v[i])
prime[++m]=v[i]=i;
for(int j=1;j<=m;j++){
if(prime[j]>v[i]||prime[j]>n/i)
break;
v[i*prime[j]]=prime[j];
}
}
}
```
$prime_{1\sim m}$ 就是 $n$ 以内的质数。
## 质因数分解
$O(\sqrt N)$。
```cpp
int m;
int factor[1600];
void q_factor(int n){
m=0;
for(int i=2;i*i<=n;i++)
while(!(n%i))
n/=(factor[++m]=i);
if(n>1)
factor[++m]=n;
}
```
$factor_{1\sim m}$ 就是分解后的结果。
### Pollard-Rho 算法
可在 $O(n^{\frac{1}{4}})$ 的时间内求出 $n$ 的最大质因子。
需要[Miller-Rabin 算法](https://www.luogu.com.cn/blog/chenjunxiu/zhi-shuo)。
## 宏定义
- $\texttt{\#define get(x)}$:$1\sim x$ 的随机数。
## 变量
- $\texttt{long long ans}$:最大质因子。
## 函数
- $\texttt{Pollard\_Rho()}$:初始化随机种子。
- $\texttt{ll gcd(ll x,ll y)}$:$x,y$ 的最大公约数。
- $\texttt{ll work(ll x,ll y)}$:将 $x$ 分解为两数之积。
- $\texttt{void solve(ll x,int t)}$:计算 $x$ 的最大质因子。
- $\texttt{ll ask(ll x))}$:询问 $x$ 的最大质因子。
## 代码
```cpp
struct Pollard_Rho{
ll ans;
Pollard_Rho(){srand(time(0));}
ll gcd(ll x,ll y){
return y?gcd(y,x%y):x;
}
#define get(x) (1LL*rand()*rand()*rand()*rand()%(x)+1)
ll work(ll x,ll y){
int t=0,k=1;
ll v0=get(x-1),v,d,s=1;
v=v0;
while(1){
v=(mul(v,v,x)+y)%x;
s=mul(s,abs(v-v0),x);
if(!(v^v0)||!s)return x;
t++;
if(t%127==0)if((d=gcd(s,x))^1)return d;
if(t==k){
if((d=gcd(s,x))^1)return d;
v0=v;k<<=1;
}
}
}
void solve(ll x,int t){
if(!(x^1)||x<=ans)return;
if(Miller_Rabin(x)){
if(ans<x)ans=x;
return;
}
ll y=x;
do{y=work(x,t--);}while(!(y^x));
while(!(x%y))x/=y;
solve(x,t);solve(y,t);
}
ll ask(ll x){
ans=0;solve(x,302627441);return ans;
}
}pr;
```
[back](https://www.luogu.com.cn/article/dltjrzaa)