素数筛笔记

放原题:

对于任何大于或等于4的偶数n,存在至少一对素数p1和p2,使得n = p1 + p2

  1. 没有人确定这个猜想是否真的成立。然而,对于给定的偶数,可以找到这样的一对素数(如果有的话)。这里的问题是编写一个程序,打印出满足给定偶数的猜想条件的所有素数对数。一个偶数序列作为输入。可以有很多这样的数字。对应于每个数字,程序应输出上述对的数量。请注意,我们只统计不同的数对,因此不应将(p1,p2)和(p2,p1)分别计为两对不同的对。

 

 

 

 2的15次方约3万左右,这里如果用一般的方法如

复制代码
#include <stdio.h>
int main(){
    int n;
    for(;;){
        scanf("%d",&n);
        if(n==0)break;
        int cp=0;//用于记录组数;
        for(int i=2;i<=n/2;i++)//因为题目说(p1,p2)和(p2,p1)等同,所以判断小于n/2部分的数即可;
        {
            int j;
            int sign=0;
            for(j=2;j<=i/j;j++)
                //判断i是否为素数,这里的语句二j<=sqrt(i)和j<=i/2亦可但是用i<=n/i最佳
                //因为j<=i/j和j<=sqrt(i)在运行时次数相同但是不用调用sqrt函数,所以会更快一点;
                {
                if(i%j==0){sign=1;break;}
            }
            if(sign==1)continue;
            for(j=2;j<=(n-i)/j;j++){
                if((n-i)%j==0){sign=1;break;}
            }
            if(sign==0)cp++;
        }
        printf("%d\n",cp);
    }
}
复制代码

 

 

 

会出现运行超时的情况,为了进一步缩短运行的耗时,可以先求出所给数据范围中的所有素数并将其存于数组当中,

直接从数组中寻找满足条件素数组,就可以不用进行重复的判断素数的运算了;

所以我们要先得到一份素数表,这里用到埃氏筛法和线性筛法来获得1到4万的素数表:(这里要补充知识:

 

 

 

1.埃氏筛法:

复制代码
#include<stdio.h>
#include <stdbool.h>
#include<math.h>
bool vis[40000];
//false表示是素数,否则不是素数(这里就是定义了一个初始值全部为0的数组
//后面会将下标不是素数的值都改成true(1))
int prime[10000];//存储素数
int cnt = 0;//下标,同时可以计算素数的个数
int main() {
    int n = 2 << 14;//这里表示2的15次方,亦可写成pow(2,15)
    for (int i = 2; i <= n; i++) {//判断2到n中的素数
        if (!vis[i]) prime[cnt++] = i;//存储素数
        for (int j = i + i; j <= n; j += i) {
            vis[j] = true;//所有素数的倍数都不是素数 
        }//数组vis[40000]中每一个变量的初始值都为0(false),如果j是某个素数的倍数那么j就一定不是素数,令vis[j]
        //等于1(true)则可以让false表示是素数,否则不是素数;
    }
    int x;
    while (scanf("%d", &x)) {
        int cp = 0;
        if (x == 0) break;
        for (int i = 0; prime[i] <= x / 2; i++)//直接利用素数表进行判断;
            if (!vis[x - prime[i]]) cp++;
        printf("%d\n", cp);
    }
    return 0;
}
复制代码

 

2.线性筛法:

复制代码
#include<stdio.h>
#include <stdbool.h>
#include<math.h>
bool vis[40000];//和上面埃氏筛法一样
int prime[10000];
int cnt = 0;
int main() {
    int n = pow(2, 15);//2 << 14
    for (int i = 2; i <= n; i++) {
        if (!vis[i]) prime[cnt++] = i;
        for (int j = 0; prime[j] <= n / i; j++) {
            vis[prime[j] * i] = true;
            if (i % prime[j] == 0) break;//(这里开始是和埃氏筛法不一样的地方)
            //这一步是干什么的呢,如果i % prime[j] == 0,prime[j]<i(一定)
            //prime[j]只能是i的最小质因数,也是i*prime[j]的最小质数
            //prime里面的质数是由小到大存储的,如果只有当前的prime[j]能整除i,那么先前的prime都不能
            //所以先前的prime都不是i的因数,而后面的prime[j]也不可能作为i的最小质因数因为后面的prime比当前的prime[j]大
            //可以直接break了
            //当i % prime[j] != 0时,也就是说prime[j]比i的最小质因数小,且prime[j]是prime[j]*i的因数
            //所以prime[j]就是prime[j]*i的最小质因数
                                      

            //以上就是助教对这段代码的解释啦,这里补充一下我的理解:
            //为了更好地理解这行代码也可以自己在纸上尝试运算哦
            //首先,prime[0]=2,此时i=2,就让vis[prime[0]*2]也就是令vis[4]=1,又刚好2%prime[0]=0;所以
            //2就是2的最小质因数,又找到了4这个以2为最小质因子的质数,然后进入下一个循环i=3
            //在这个循环中prime[0]*i=2*3=6,和prime[1]*i=3*3=9找到6和9这个两个非质数然后进入下一个循环;
            //找到2*4=8这个非素数
            //到这里,可以观察到当i不是素数时当prime[j]取得i的最小质因数就会进入下一个循环
            //这样就能保证prime[j]*i得到的结果的最小质因数就是prime[i],可以避免重复运算
            //当i是一个素数时语句vis[prime[j] * i] = true;会一直执行到prime[j]=i;
            //这时就会把i*i这个非素数筛掉,利用这种方法就能在prime[j] <= n / i这个语句满足时找到所有比n小的素数
            //(毕竟prime[j] <= n / i把不等号两边同时乘i就能得到prime[j] * i<=n嘛,就把所有素数都找到了)
            //对比埃氏筛法,在个算法没有重复运算,所以能够做到运行速度更快
        }
    }
    int x;
    while (scanf("%d", &x)) {
        if (x == 0) break;
        int cnt = 0;
        for (int i = 0; prime[i] <= x / 2; i++)
            if (!vis[x - prime[i]]) cnt++;
        printf("%d\n", cnt);
    }
    return 0; }
复制代码

 



 

 

可以明显看到这样子运行速度就快很多啦

 

————————————————
本文参考CSDN博主「不依法度」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/weixin_50816938/article/details/127191347

posted @   唐锴烨  阅读(71)  评论(2编辑  收藏  举报
相关博文:
阅读排行:
· 微软正式发布.NET 10 Preview 1:开启下一代开发框架新篇章
· 没有源码,如何修改代码逻辑?
· PowerShell开发游戏 · 打蜜蜂
· 在鹅厂做java开发是什么体验
· WPF到Web的无缝过渡:英雄联盟客户端的OpenSilver迁移实战
点击右上角即可分享
微信分享提示