素数个数-欧拉筛法

模拟的时候真没想到这是一道这么麻烦的题。。。

先来看题:

素数个数

题目描述

1,2,\cdots,N1,2,,N 中素数的个数。

输入输出格式

输入格式:

1 个整数N

输出格式:

1 个整数,表示素数的个数。

 输入输出样例

输入样例#1:  复制
10
输出样例#1:  复制
4

说明

• 对于40% 的数据,1 \N \10^6

• 对于80% 的数据,1 \lN \10^7

• 对于100% 的数据,1 \N \10^8 

当看到这道题的时候,我直接写下了逐个取余判断的代码,然而看到数据范围的时候我就慌了。。。

当时由于没学过关于素数筛选的算法,很显然除了打表我什么也干不了。。。

好吧,不废话了,直接步入正题:

由于这道题的数据范围较大,因此枚举肯定是行不通的,就算是使用正常筛法,面对10的八次方的数据也是显得十分的吃力,由此,我们要引入欧拉筛法(这里先介绍一下朴素筛法):

由于任何合数都可以拆分成若干素数的乘积,因此每当我们找到一个素数的时候,就可以将部分合数筛选出来,那么我们就解决的部分的问题。

并且讲到这里有一个隐含条件:就是没有被筛选出来的数就是素数(这个可以简化代码长度,也是理解这道题的关键),至于证明,我们可以用反证法:如果它不是素数,那么它一定是合数,而合数又可以拆分成两个素数的乘积,那么在找到它的因子的时候就一定会将其筛选出来。 证完

强忍着没有在证明的时候说显然。。。

但是,可能大家在证明的时候会发现一个问题:就是一个合数一次拆分时的素数因子可能不止一个,那么不就重复计算了吗?

因此我们要优化!!!(由此诞生了欧拉筛法)

由于下面讲解问题,我们先看代码:

 1 #include<cstdio>
 2 #include<cmath>
 3 int prime[100000005];
 4 bool vis[100000005];
 5 int Prime(int n)
 6 {
 7     int ans=0;
 8     for(int i=2; i<=n; i++)
 9     {
10         if(!vis[i])
11             prime[ans++]=i;
12         for(int j=0; j<ans&&i*prime[j]<=n; j++)
13         {
14             vis[i*prime[j]]=1;
15             if(i%prime[j]==0)
16                 break;
17         }
18     }
19     return ans;
20 }
21 int main()
22 {
23     int n;
24     scanf("%d",&n);
25     printf("%d",Prime(n));
26     return 0;
27 }

其实也很好理解:由于合数可以拆分成不同素数*k(k∈Z),那么当我们筛选时只要筛选出最小素数因子即可,比如12

12=4*3=6*2 很显然,我们需要的就是用素数2来筛选掉12,那么怎么实现呢?

其实这其中有一个规律:由于i是由小到大枚举的,并且数组中的素数也是由小到大枚举的,那么显然我们会先看到4*3,又因为4在和素数2筛掉8时发现竟然能被整除,并且接下来的4*3是没有意义的计算,那么每当i%素数==0是跳出即可。

如果你要问我具体该如何证明那我可以简单说一下:

由于p[j]*k(k∈Z)=i,那么a[j+1]*i=a[j]*k*a[j+1],所以a[j]乘以某个数一定在将来会把这个合数筛掉,由于a[j]比a[j+1]小,那么a[j]才可能是最小素数因子。

好的,我讲完了(好长啊qwq)

猜你喜欢

转载自www.cnblogs.com/yufenglin/p/10124087.html
今日推荐