素数&筛法

素数

素数与合数

•若一个大于一的正整数P,其约数只有1和P本身,称其为素数(质数)。若其有超过两个约数,则为合数。

素数无限定理

•正整数集中包含无限个素数。

•反证:假设素数有限,设其为(p_1~p_n),构造(s=1+π(pi)),若s是素数,矛盾;若s是合数,则((p_1-p_n))都不是(s)的约数,与算术基本定理矛盾。

•特别的:1~n中大约有(O(n/ln(n)))个素数

互质

•若两个数A,B其最大公约数为1,则称A,B互质。

•此时k*A(0<=k<B)会遍历整个mod B剩余系。

•若A,B最大公约数为g,则k*A只能遍历mod B剩余系中g的倍数部分。

筛法

朴素筛——(O(sqrt n))

注意循环里千万不能有sqrt

for(int i=2;i*i<=n;i++)
    if(n%i==0) {prime[++prime[0]]=i;while(n%i==0)n/=i;}
if(n>1)prime[++prime[0]]=n;

Eratosthenes 埃氏筛

这个不常用

void Era(int n)  {
	for(int i=0;i<=n;i++) is_prime[i]=1;
	is_prime[0]=is_prime[1]=0;
	for(int i=2;i<=n;i++) {
		if(is_prime[i]) {
			p[++p[0]]=i;
			if((ll)i*i<=n) {
				for(int j=i*i;j<=n;j+=i)
					is_prime[j]=0;
			} 
		}
	}
}

欧拉线性筛——(O(n))

•维护每个数的最小素因子mindiv,从2开始,若其mindiv未知,则为素数,将其小于本身的素数倍的mindiv设置成素数。

•每个数只会被其最小素因子筛一次。

#include <iostream>
#include <cstdio>
using namespace std;
const int N=100000001;
int n;
int mindiv[N],prime[N];
int main(){
    scanf("%d",&n);
    for(int i=2;i<=n;i++){
        if(!mindiv[i]) mindiv[i]=prime[++prime[0]]=i;
        for(int j=1;j<=prime[0]&&i*prime[j]<=n&&prime[j]<=mindiv[i];j++)
            mindiv[i*prime[j]]=prime[j];
    }
    for(int i=1;i<=prime[0];i++)
        printf("%d
",prime[i]);
    return 0;
}

线性筛还可以筛 φ ,d (约数个数), sd(约数和)

void PHI(int x){
    phi[1]=1;
    for(int i=2;i<=x;i++){
        if(!mindiv[i]) mindiv[i]=p[++p[0]]=i,phi[i]=i-1;
        for(int j=1;j<=p[0]&&i*p[j]<=x;j++){
            mindiv[i*p[j]]=p[j];
            if(i%p[j]==0){
                phi[i*p[j]]=phi[i]*p[j];
                break;
            }else phi[i*p[j]]=phi[i]*phi[p[j]];
        }
    }
}

d和sd的证明见这篇博客

#include<cstdio>
#include<algorithm>
typedef long long ll;
inline int gi(){
    int x=0,f=1;char ch=getchar();
    while(ch<'0'||ch>'9')f=ch=='-'?-1:f,ch=getchar();
    while(ch>='0'&&ch<='9')x=x*10+ch-'0',ch=getchar();
    return x*f;
}
const int N=1000001;
int prime[N],d[N],f[N];
bool mindiv[N];
int main(){
    int n=gi();
    for(int i=1;i<=n;++i) d[i]=1;
    for(int i=2;i<=n;++i){
        if(!mindiv[i]) prime[++prime[0]]=i,d[i]=2,f[i]=1;
        for(int j=1;i*prime[j]<=n&&j<=prime[0];++j){
            mindiv[i*prime[j]]=1;
            if(i%prime[j]==0){
                f[i*prime[j]]=f[i]+1;
                d[i*prime[j]]=d[i]/(f[i]+1)*(f[i*prime[j]]+1);
                break;
            }
            f[i*prime[j]]=1;
            d[i*prime[j]]=d[i]*d[prime[j]];
        }
    }
    ll ans=0;
    for(int i=1;i<=n;++i) ans+=d[i];
    printf("%lld
",ans);
    return 0;
}
    sd[1]=1;
    for(int i=2;i<=n;++i){
        if(!mindiv[i]) prime[++prime[0]]=i,sd[i]=sp[i]=i+1;
        for(int j=1;i*prime[j]<=n&&j<=prime[0];++j){
            mindiv[i*prime[j]]=1;
            if(i%prime[j]==0){
                sp[i*prime[j]]=sp[i]*prime[j]+1;
                sd[i*prime[j]]=sd[i]/sp[i]*sp[i*prime[j]];
                break;
            }
            sp[i*prime[j]]=1+prime[j];
            sd[i*prime[j]]=sd[i]*sd[prime[j]];
        }
    }

例1:计算区间中素数的个数

(L≤R≤2147483647,R-L≤1000000)

上面复杂度最优的欧拉筛也只能40。。。还是只用了一遍加二分查找

正解:

筛出1-50000中的所有质数,并且对合数打上标记.

在L-R的范围呢用我们已求出的质数筛出其中的合数(设p为质数,则i*p一定不为质数),并对其打上标记(埃氏筛的思想)

遍历L-R,没有打标记的元素即为我们所求的素数

#include <iostream>
#include <cstdio>
using namespace std;
const int maxn=1e7+1;
#define int long long
int L,R;
int prime[maxn],cnt,ans;
int mindiv[maxn];
bool vis[maxn];
void Gprime(){
    for(int i=2;i<=50000;i++){
        if(!mindiv[i]) mindiv[i]=prime[++prime[0]]=i;
        for(int j=1;j<=prime[0]&&i*prime[j]<=50000&&prime[j]<=mindiv[i];j++)
            mindiv[i*prime[j]]=prime[j];
    }
}
signed main(){
    scanf("%lld%lld",&L,&R);
    L=L==1?2:L;
    Gprime();
    for(int i=1;i<=prime[0];++i){
        int p=prime[i],start=(L+p-1)/p*p > 2*p ? (L+p-1)/p*p : 2*p;//注意最小为2p
        for(int j=start;j<=R;j+=p)vis[j-L+1]=1;
    }
    for(int i=1;i<=R-L+1;++i)if(!vis[i])ans++;
    printf("%lld
",ans);
}

例2:Prime Distance

Description

•给定L,R<=2147483647,L-R<1e6 求区间([L,R])中所有相邻的素数中差最小与最大的素数对

Solution

(和上一题完全一样的思路....)要找到 ([l, r]) 中相邻最近和最远的素数对肯定是需要找出 ([l, r]) 内所有素数 . 但是无论是直接线性打表还是暴力都处理不了int的数据.素数 p 只对 (>=p^2)的数有贡献,可以先给 (sqrt r) 内的素数打个表, 再用 (sqrt r) 内的素数去筛选 ([l, r]) 内的合数, (模拟埃氏筛)然后再遍历一次 [l, r], 记录答案即可;复杂度 : $sum_{p<=sqrt(r)} frac{R-L}{p} $ p是素数,

例3:质数生成器

Description

要求生成质数的范围是([m,n]) ((1 <= m <= n <= 10^9, n-m<=1000000))

Solution

与 1 思路完全一样

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
#define N 10000003
using namespace std;
int pd[N],prime[N],T,l,r,vis[N];//prime[0]
void init(){
	pd[1]=1;
	for(int i=2;i<=100000;i++){
		if(!pd[i])prime[++prime[0]]=i;
		for(int j=1;j<=prime[0];j++){
			if(i*prime[j]>100000)break;
			pd[prime[j]*i]=1;
			if(i%prime[j]==0)break;
		}
	}
}
int main(){
	init();
	scanf("%d",&T);
	for(int i=1;i<=T;i++){
		scanf("%d%d",&l,&r);
		for(int j=1;j<=prime[0];j++){
			int t=(l-1)/prime[j]+1;
			if(t<=1)t=2;
			for(int k=t*prime[j];k<=r;k+=prime[j])
				vis[k-l+1]=i;
		}
		for(int j=1;j<=r-l+1;j++)
			if(vis[j]!=i && j+l-1!=1)printf("%d
",j+l-1);
		puts("");
	} 
	return 0;
} 

Miller-rabin素数测试

https://www.cnblogs.com/clockwhite/p/12148877.html

Pollard-Rho算法

https://www.luogu.com.cn/blog/JasonZhou200337/pollard-rho-suan-fa-jian-jie

dingding 10-1 18:30

#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<algorithm>
using namespace std;
#define il inline
typedef long long LL;
inline LL read() {
	LL x=0;char ch=getchar();
	while(!isdigit(ch))ch=getchar();
	while(isdigit(ch)){x=x*10+ch-'0';ch=getchar();}
	return x;
}
il LL qmul(LL a,LL b,LL md) {
	LL c=(long double)a*b/md+0.5;
	c=a*b-c*md;
	return c<0?c+md:c;
}
il LL qpow(LL a,LL b,LL md) {
	LL r=1;
	for(;b;b>>=1,a=qmul(a,a,md)) if(b&1) r=qmul(r,a,md);
	return r;
}
il bool check(LL a,LL p,LL md) {
	LL t=qpow(a,p,md);
	if(t==md-1) return 1;
	if(t==1) return p&1?1:check(a,p/2,md);
	return 0;
}
il bool Miller_Rabin(LL n) {
	if(n<=1) return 0;
	if(n<=3) return 1;
	if((n&1)==0||n%3==0) return 0;
	static int p[]={2,3,5,7,11,13,17,19};
	for(int i=0;i<8;++i)
		if(n==p[i]) return 1;
		else if(!check(p[i],n-1,n)) return 0;
	return 1;
}
il LL gcd(LL a,LL b) {
	return b?gcd(b,a%b):a;
}
LL rho(LL n) {
	LL a=((LL)rand()<<30^rand())%n;
	LL x=1,y=0,p,t;
	for(int k=1;;y=x,k<<=1) {
		p=1;
		for(int i=0;i<k;++i) {
			x=qmul(x,x,n)+a,x>=n?x-=n:0;
			p=qmul(p,abs(x-y),n);
		}
		if((t=gcd(p,n))>1) return t;
	}
}
LL mxdiv;
void divi(LL n) {
	if(n==1) return;
	if(Miller_Rabin(n)) {
		mxdiv=max(mxdiv,n);
		return;
	}
	LL t;
	do t=rho(n); while(t==n);
	while(n%t==0) n/=t;
	divi(n),divi(t);
}
int main() {
    int T; LL n; T=read();
    while(T--) {
    	n=read();
    	if(Miller_Rabin(n)) {puts("Prime"); continue;}
    	mxdiv=0;
    	divi(n);
    	printf("%lld
",mxdiv);
    }
    return 0;
}

原文地址:https://www.cnblogs.com/ke-xin/p/13531924.html