质数知识(筛法,质因子分解)

质数介绍

质数:只有两个正因数(1和自己)的自然数即为质数。比1大但不是素数的数称为合数。1和0既非素数也非合数。

质数的一些性质:

  • N以内的素数的个数随着N的增大趋近于log(n)
  • 从不大于n的自然数随机选一个数,它是素数的概率大约是1/In n(素数定理)
  • 随着n的增大素数越来越稀疏。
  • 在一个大于1的数 a和它的2倍之间(即区间(a,2a]中)必存在至少1个素数。

1.埃氏筛法

埃氏筛法:

const int maxn=10001;
int prime[maxn],num=0;
bool p[maxn];
void Find_Prime(){
	memset(p,0,sizeof p);//默认全素数 
    for(int i=2;i<maxn;i++)  
        if(p[i]==false){
            prime[num++]=i;
            for(int j=i+i;j<maxn;j+=i) p[j]=true;
        }
}

例题:素数

题目介绍:

输入一个整数n(2<=n<=10000),要求输出所有从1到这个整数之间(不包括1和这个整数)个位为1的素数,如果没有则输出-1。

思路:

筛法枚举所有素数,然后vector存储每组数据答案,便于输出

代码:

#include<bits/stdc++.h>
#define LL long long
#define fo(i,a,b) for(int i=a;i<b;i++)
#define js ios::sync_with_stdio(false);cin.tie(0); cout.tie(0)
using namespace std;
const int maxn=10001;
int prime[maxn],num=0;
bool p[maxn];
vector<int> v;
void Find_Prime(){
	memset(p,0,sizeof p);
    for(int i=2;i<maxn;i++)  
        if(p[i]==false){
            prime[num++]=i;
            for(int j=i+i;j<maxn;j+=i) p[j]=true;
        }
}//埃氏筛
int main(){
    int n;
    Find_Prime();
    while(~scanf("%d",&n)){
        v.erase(v.begin(),v.end());
        int count=0;
        for(int i=0;i<maxn;i++){
            if(prime[i]<n&&prime[i]%10==1){
                count++;
                v.push_back(prime[i]);
            }
            else if(prime[i]>=n) break;
        }
        int len=v.size();
        for(int i=0;i<len;i++){
            if(i!=len-1)printf("%d ",v[i]);
            else printf("%d",v[i]);
        }
        if(count==0) printf("-1");
        printf("\n");
    }
    return 0;
}

2.欧拉筛法

首先,我们知道当一个数为素数的时候,它的倍数肯定不是素数。所以我们可以从2开始通过乘积筛掉所有的合数。 将所有合数标记,保证不被重复筛除,时间复杂度为 O ( n ) O(n) O(n)

#include <bits/stdc++.h>
#define ll long long
const int maxn=1e6+10;
using namespace std;
bool vis[maxn];
ll prime[maxn];
int tot=0;
void init(){//O(n)欧拉筛
    vis[1]=1;//突出1不是素数,可不写
    for(int i=2;i<maxn;i++){
        if(!vis[i]) prime[++tot]=i;
        for(int j=1;j<=tot&&i*prime[j]<maxn;j++){
            vis[i*prime[j]]=1;
            if(i%prime[j]==0) break;
        }
    }
}
int main(){
    init();//找出maxn-1以内的素数 
    for(int i=1;i<10;i++)printf("%d ",prime[i]);//输出前9位素数 
    return 0;
}

解释:

  • v i s [ i ∗ p r i m e [ j ] ] = 1 ; vis[i*prime[j]]=1; vis[iprime[j]]=1; 这里不是像埃氏筛一样用 i i i 的倍数来消去合数,而是把 prime里面记录的素数,升序来当做要消去合数的最小素因子,这里 i i i 在消去合数中的作用是当做倍数的
  • i i i p r i m e [ j ] prime[j] prime[j]的整数倍 (i%prime[j]==0) 时, i ∗ p r i m e [ j + 1 ] i*prime[j+1] iprime[j+1]肯定被筛过,跳出循环。

例题:
在这里插入图片描述

思路(借鉴dsy):
在这里插入图片描述
代码:

#include<bits/stdc++.h>
using namespace std;
int n;
const long long mod = 1e9+7;
bool b[80000100];
int  p[10000000];
int cnt = 0;
long long ksm(int a, int b){
    long long ans= 1, t= a;
    while(b){
        if (b & 1) ans = ans * t % mod;
        t = t * t %mod;
        b >>= 1;
    }
    return ans;
}
long long calc(int x){
    if (x == 2)
        return ksm(2, floor(log(n/3)/ log(2)));
    return ksm(x, floor(log(n/2)/ log(x)));
}
int main(){
    cin>> n; 
    long long ans = 1;
    memset(b, 1, sizeof(b));//初始全素数
    for(int i = 2;i <= n/2; i++){//筛到n/2即可 
        if(b[i]){
            p[cnt++] = i;
            ans = (ans * calc(i)) % mod;//计算贡献 
        }
        for(int j = 0;j < cnt && i*p[j] <= n/2; j++){
            b[i*p[j]] = 0;//筛去合数 
            if(i % p[j] == 0) break;
        }
    }
    if (ans == 1) cout <<"empty";
    else cout <<ans;
    return 0;
}

两种筛法区别

埃氏筛法和欧拉筛法的区别

例题

1059 Prime Factors (25分)

1059 Prime Factors (25分)

法一(无筛法)

#include<bits/stdc++.h>
#define js ios::sync_with_stdio(false);cin.tie(0); cout.tie(0)
using namespace std;
const int maxn = 100010;
bool is_prime(int n){
    if(n == 1) return false;
    int sqr = (int)sqrt(1.0*n);
    for(int i = 2; i <= sqr; i++){
        if(n % i == 0) return false;
    }
    return true;
}
int prime[maxn],pNum = 0;
void Find_prime(){
    for(int i = 1 ; i < maxn; i++){
        if(is_prime(i) == true){
            prime[pNum++] = i;
        }
    }
}
struct facot{
    int x,cnt;
}fac[10];//x为质因子,cnt为其个数 
int main(){
    Find_prime();
    int n;
    scanf("%d",&n);
    int num = 0;
    if(n == 1) printf("1=1");
    else{
        printf("%d=",n);
        int sqr = (int)sqrt(1.0*n);
        for(int i = 0; i < pNum ; i++){
            if(n % prime[i] == 0){
                fac[num].x = prime[i];
                fac[num].cnt = 0;            
                while(n % prime[i] == 0){
                    fac[num].cnt++;
                    n /= prime[i];
                }
                num++;
            }
            if(n == 1) break;
        }
        if(n != 1){//寻找是否有唯一大于根号n的质因子 
            fac[num].x = n;
            fac[num].cnt = 1;
        }
        for(int i = 0; i < num; i++){
            if(i > 0) printf("*");
            printf("%d",fac[i].x);
            if(fac[i].cnt > 1) printf("^%d",fac[i].cnt);
        }
    }
    return 0;
}

法二(埃氏筛)

#include<bits/stdc++.h>
#define js ios::sync_with_stdio(false);cin.tie(0); cout.tie(0)
using namespace std;
const int maxn = 100010;
int prime[maxn],pNum = 0;
void Find_prime(){
    int p[maxn];
    memset(p,0,sizeof(p));//默认全是素数 
    for(int i=2;i<maxn;i++){
        if(p[i]==0){
            prime[pNum++]=i;
            for(int j=2*i;j<maxn;j+=i)p[j]=1;
        }
    }
} 
struct facot{
    int x,cnt;
}fac[10];//x为质因子,cnt为其个数 
int main(){
    Find_prime();
    int n;
    scanf("%d",&n);
    int num = 0;
    if(n == 1) printf("1=1");
    else{
        printf("%d=",n);
        int sqr = (int)sqrt(1.0*n);
        for(int i = 0; i < pNum ; i++){
            if(n % prime[i] == 0){
                fac[num].x = prime[i];
                fac[num].cnt = 0;            
                while(n % prime[i] == 0){
                    fac[num].cnt++;
                    n /= prime[i];
                }
                num++;
            }
            if(n == 1) break;
        }
        if(n != 1){//寻找是否有唯一大于根号n的质因子 
            fac[num].x = n;
            fac[num].cnt = 1;
        }
        for(int i = 0; i < num; i++){
            if(i > 0) printf("*");
            printf("%d",fac[i].x);
            if(fac[i].cnt > 1) printf("^%d",fac[i].cnt);
        }
    }
    return 0;
}

小W 的质数(prime)

Description

小X是一位热爱数学的男孩子,在茫茫的数字中,他对质数更有一种独特的情感。小X认为,质数是一切自然数起源的地方。

在小X的认知里,质数是除了本身和1以外,没有其他因数的数字。但由于小X对质数的热爱超乎寻常,所以小X同样喜欢那些虽然不是质数,但却是由两个质数相乘得来的数。  于是,我们定义,一个数是小X喜欢的数,当且仅当其是一个质数,或是两个质数的乘积。  而现在,小X想要知道,在L到R之间,有多少数是他喜欢的数呢?

Input

第一行输入一个正整数Q,表示询问的组数。  
接下来Q行,包含两个正整数L和R,保证L≤R。

Output

输出Q行,每行一个整数,表示小X喜欢的数的个数。

Input

10
282 491
31 178
645 856
227 367
267 487
474 697
219 468
582 792
315 612
249 307

Output

97
78
92
65
102
98
114
90
133
29

思路:

这道题需要欧拉筛(线性筛),因为线性筛就是筛出素数并且剔除掉素数与另一个数 i i i 的乘积,对于两个素数的乘积,我们只需要判断一下 i i i 是不是素数就好了,不过因为询问较多,最后要用前缀和处理一下.

代码:

#include <iostream>
#include <cstdio>
#include <cmath>
#include <algorithm>
#include <queue>
#include <cstring>
#include <set>
#include <map>
#include <sstream>
#define LL long long
#define mem(f, x) memset(f,x,sizeof(f)) 
#define fo(i,a,n) for(int i=a;i<n;++i)
#define fo2(i,a,n) for(int i=a;i<=n;++i)
using namespace std;
template<class T>inline void read(T &x){
    x=0; char c=getchar(); bool f=0;
    while(!isdigit(c))f^=c=='-',c=getchar();
    while(isdigit(c))x=(x<<3)+(x<<1)+(c^48),c=getchar();
    if(f)x=-x;
}
LL f[10000005], prime[10000005], tot = 0;
//vis:素数,vis2:素数乘积 
LL vis[10000005], vis2[10000005];
long long sum[10000005];

struct node{
    int l, r;
}a[100005];

void init(int r){//欧拉筛
    fo2(i,2,r){
        if (!f[i]){//发现素数了 
            prime[++tot] = i;
            vis[i] = 1;
        }
        fo2(j,1,tot){
            int t = i * prime[j];
            if (t > r)break;
            f[t] = 1;
            if (vis[i])vis2[t] = 1;//统计素数和素数乘积 
            if (i % prime[j] == 0)break;
        }
    }
}
int main(){
	int q;
	LL maxn=0;
    read(q);
    fo2(i,1,q){
        read(a[i].l);read(a[i].r);
        maxn = max(maxn,1LL*a[i].r);
    }
    init(maxn);
    //前缀和实现记忆化
    fo2(i,2,maxn)sum[i] = sum[i - 1] + max(vis[i],vis2[i]);
    fo2(i,1,q)printf("%lld\n", sum[a[i].r] - sum[a[i].l - 1]);
    return 0;
}

斐波那契数列(质因子分解)

斐波那契数列(升级版)

代码:

//分解质因数的好方法 
#include<bits/stdc++.h> 
using namespace std; 
long long n,f[49],x=1;
const long long p=pow(2,31);
int main(){
    cin>>n;
    f[1]=1;
    f[2]=1;
    for(int i=3;i<=n;i++)
        f[i]=(f[i-1]+f[i-2])%p;
    f[n]%=p;
    cout<<f[n]<<"=";
    for(int i=2;i<=f[n];i++){
        while(!(f[n]%i)){//寻找因子
            if(x){x=0;cout<<i;}
            else cout<<'*'<<i;
            f[n]/=i;
        }//这种方法保证寻找的都是质因子 
    }
    cout<<endl;
}
  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值