欢迎关注微信公众号“Python小灶,和我一起每天学习Python新知识”
题目
质数也叫素数,求给定数值N以内的所有素数(只能被1和他本身整除的数),2是素数。
输入格式:
输入在一行中给出一个正整数N(1<N<100000000(1亿))。
输出格式:
输出质数总个数。
输入样例:
10
输出样例:
4
解题思路
1、暴力枚举,两重循环,外层循环控制从1到N的所有数,内层循环判断是不是质数(单独封装成函数),
判断质数很简单,找到一个除了1和他本身以外的因数就可以反证。因数的分布遵从 因数1 <= sqrt(n),
因数2 >= sqrt(n) ,随便想个数推算一下就行,很好理解。
当N较小时,可以用这个方法,但是当N较大时,将十分耗费时间到不可接受的地步。
代码
#include <stdio.h>
#include <math.h>
int isPrime(int n)
{
int ret = 1;
if (n == 2){
return ret;
}
for(int i=2; i<(sqrt(n) + 1); i++){
if (n%i == 0){
ret = 0;
break;
}
}
return ret;
}
int main()
{
int N = 0;
int cnt = 1;
scanf("%d", &N);
for(int i=2; i<=N; i++){
if (isPrime(i) == 1){
cnt++;
}
}
printf("%d\n", cnt);
return 0;
}
2、埃拉托斯特尼筛法:
2是最小的质数,2的倍数都是合数(因为都能被2整除);
同理3的倍数也都是合数,以此类推。
所以我们给定一个布尔型数组,默认全零,然后从2开始,只要数组对应的值是0,我就判断它是不是质数,如果是,按照埃拉托斯特尼筛法把它的倍数全改为1,这样后面这个位置不需要再判断。
其中有个小技巧,我们可以写成(int j=ii; j<=N; j+=i) 而不是 (int j=2×i; j<=N; j+=i),这是因为例如5,只要标记55,56,57…为合数,因为52,53,5*4…已经被之前出现的数的倍数标记过了
代码
#include <stdio.h>
#include <math.h>
_Bool prime[100000000];
// 函数描述
int isPrime(int n)
{
int ret = 1;
if (n == 2){
return ret;
}
for(int i=2; i<(sqrt(n) + 1); i++){
if (n%i == 0){
ret = 0;
break;
}
}
return ret;
}
int main()
{
int N = 0;
int cnt = 0;
scanf("%d", &N);
for(int i=2; i<=N; i++){
if (prime[i] == 0){
if (isPrime(i) == 1){
cnt++;
for (int j=i*i; j<=N; j+=i){
prime[j] = 1;
}
}
}
}
printf("%d\n", cnt);
return 0;
}
优化版
_Bool prime[100000000];
// 函数描述
int main()
{
int N = 0;
int cnt = 0;
scanf("%d", &N);
for(int i=2; i<=N; i++){
if (prime[i] == 0){
cnt++;
}
for (int j=2; i*j<=N; j++){
prime[i*j] = 1;
}
}
printf("%d\n", cnt);
return 0;
}
3、欧拉筛:
核心代码
for (int j=0; j<count&&p[j]i<=N; j++){
prime[p[j]i] = 1;
if(i%p[j] == 0){
break;
}
}
其中if(i%p[j] == 0) break;保证了每一个合数只被他的最小质因数筛选一次。
why?
该行保证了每一个合数只被它的最小质因数筛一次。
例如当i%prime[j]==0时,则iprime[j+1]一定是prime[j]的整数倍,因为i可以整除prime[j],并且iprime[j+1]/prime[j]>i所以在当前i中,iprime[j+1]还不能被筛,因为当前的i无法使得iprime[j+1]/prime[j]等于i*prime[j+1]的最小质因数。
代码
_Bool isPrime[100000000];
int prime[6000000] = {0};
// 函数描述
int main()
{
int N = 0;
int cnt = 0;
scanf("%d", &N);
for(int i=2; i<=N; i++){
// printf("%d,%d\n", i, isPrime[i]);
if (isPrime[i]==0) prime[cnt++] = i;
for (int j=0; j<cnt&&prime[j]*i<=N; j++){
isPrime[prime[j]*i] = 1;
if(i%prime[j] == 0) break;
}
}
printf("%d\n", cnt);
return 0;
}
优化版
_Bool isPrime[100000000];
int prime[6000000] = {0};
int main()
{
int N = 0;
int cnt = 1;
scanf("%d", &N);
for(int i=3; i<=N; i+=2){
if (isPrime[i]==0) prime[cnt++] = i;
for (int j=1; j<cnt&&prime[j]*i<=N; j++){
isPrime[prime[j]*i] = 1;
if(i%prime[j] == 0) break;
}
}
printf("%d\n", cnt);
return 0;
}