你能告诉我一亿以内有多少对孪生素数吗?

Posted na2s2o3

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了你能告诉我一亿以内有多少对孪生素数吗?相关的知识,希望对你有一定的参考价值。

所谓孪生素数,就是相差为2的素数对,例如3和5,11和13。如果仅仅是100以内的孪生素数,相信大部分人只用数就能数出来,毕竟100以内只有25个素数。但是如果是1000以内呢?100000以内呢?如果像题目中说的一样,一亿以内呢?

硬着头皮数显然不行了,要解决这个问题,我们要依赖于编程。


 

要求孪生素数的对数,首先要找到孪生素数,要找到孪生素数,首先要找到素数。C++中有许多找素数的方法,比如基础的试除法,其代码如下:

bool prime(int n) 
{
    if(n<2)  return false;
    for(int i=2;i*i<=n;i++)
    {
        if(n%i==0)  return false;
    }
    return true;
}

这段代码比较基础,也很容易理解。

完整程序如下:

#include<iostream>
#include<cstdio>
#include<algorithm>
using namespace std;
int n,cnt; //cnt记录孪生素数对数 
bool prime(int n)  //试除法筛素数 
{
    if(n<2)  return false;
    for(int i=2;i*i<=n;i++)
    {
        if(n%i==0)  return false;
    }
    return true;
}
int main()
{
    scanf("%d",&n);
    for(int i=2;i<=n;i++)
    {
        if(prime(i)&&prime(i+2))  //判断是否满足孪生素数定义 
        {
            cnt++;
        }
    }
    printf("%d
",cnt);return 0;
}

一切都很顺利的进行了,我们不禁暗想:

  孪生素数,就这???

然而,当输入“100000000”时,奇怪的事情发生了,答案久久没有出现在小黑板上,只有光标在闪动着寂寞的白光,宛若孤独而无人陪伴的我

这令人疑惑,于是我关闭了窗口,重新运行,并输入了较小的数。答案几乎是在敲回车后的一刹那出现在小黑板上。

这说明程序没有问题,输入“100000000”答案迟迟不出现,只有一个可能——程序在运算结果。

既然如此,那我们能做的就只有等待。

终于,在不知多久之后,小黑板上终于出现了我们所希望看到的东西——440312。

虽然得到了结果,但比起这个,我们更想知道它到底算了多久,于是我在程序中加入了从百度抄来的计时程序,如下:

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<time.h>  //头文件 
using namespace std;
clock_t start,finish;  //定义始终 
double duration;  //定义时间 
int n,cnt; 
bool prime(int n)  
{
    if(n<2)  return false;
    for(int i=2;i*i<=n;i++)
    {
        if(n%i==0)  return false;
    }
    return true;
}
int main()
{
    scanf("%d",&n);
    start=clock();  //在程序开始运行时开始计时,注意,若将这句话加在输入之前,会把输入数据的时间记入,影响结果 
    for(int i=2;i<=n-2;i++)
    {
        if(prime(i)&&prime(i+2))  
        {
            cnt++;
        }
    }
    finish=clock();  //结束计时 
    duration=(double)(finish - start)/CLOCKS_PER_SEC;  //计算时间 
    printf( "%f seconds
",duration);  //输出时间    
    printf("%d
",cnt);return 0;
}

当输入“10000”,运算时间为0.002000s,输入“1000000”,运算时间为0.310000s,两次运算时间并没有差很多。

但输入“100000000”,在又一次漫长的等待后,小黑板上出现了惊人的149.797000s,是计算1000000以内的孪生素数对数所用时间的约483倍。

这可怕的数字令我们感到恐惧,若是数据范围再大一些,试除法岂不是要算一年!

看来数据太大,用试除法求解是行不通了,我们需要的是效率更高的算法。


 

提到高效算法,聪明的你一定能想到Eratosthenes筛选法,翻译成人话就是埃氏筛。

埃氏筛的基本思想:素数的倍数一定不是素数。先假设所有数都是素数,从小到大枚举每一个素数x,把x的倍数都标记为非素数。当从小到大扫描到一个数x时,若它尚未被标记,则它不能被2~x-1之间的任何数整除,该数就是素数。(对整数1特殊处理)

埃氏筛代码如下:

void primes(int n)
{
    memset(v,0,sizeof(v)); //合数标记 
    for(int i=2;i<=n;i++)
    {
        if(v[i])  continue;    
        cout<<i<<endl;  //i是素数 
        for(int j=i;j<=n/i;j++)
        {
            v[i*j]=1;
        }      
    }
}

埃氏筛的时间复杂度是O(nloglogn),效率非常接近线性,是一种常用的素数筛法。然而,埃氏筛会对合数进行重复标记,即使是优化之后,其根本原因是算法不能唯一确定产生合数的方式。据此,我们在生成一个需要标记的合数时,每次只向现有的数乘上一个质因子,并且让它是这个合数的最小质因子。这相当于让合数的质因子从大到小累积。具体来说,我们采用如下的筛法:

int v[maxn],prime[maxn]; 
void primes (int n) //用线性筛找素数 
{
    memset(v,0,sizeof(v)); //最小质因子 
    m=0; //素数数量 
    for(int i=2;i<=n;i++)
    {
        if(!v[i]) //i是质数 
        {
            v[i]=i;
            prime[++m]=i;
        }
                            //给当前的数i乘上一个质因子 
        for(int j=1;j<=m;j++)
        { 
                            //i有比prime[j]更小的质因子,或者超出n的范围 
            if(prime[j]>v[i]||prime[j]>n/i)  break;
                            //prime[j]是合数i*prime[j]的最小质因子 
            v[i*prime[j]]=prime[j];
        }
    }
}

这便是线性筛。每个合数只会被它的最小质因子筛一次,时间复杂度为O(N)。

筛法介绍完了,求孪生素数对的程序也就不难写了,只需要判断与一个素数相差2的数是否为素数即可。这个任务交给读者自行完成。


下面我简要说一下测评结果。

技术图片                            技术图片

 这是使用线性筛求对数并输出孪生素数对的运行结果,总共跑了173.793000s,若仅仅输出对数,只需要1.410000s,比试除法快了近107倍。

技术图片                             技术图片

 

 埃氏筛也不敢示弱,跑出了178.203000s和3.105000s的不俗成绩。

(顺便一提,用试除法求孪生素数对,如果要输出孪生素数是什么,它需要跑约328s)

相比于基础的试除法,这两种算法的效率都高得没话说。

正所谓,永恒与刹那间,只隔着我的算法。既然有算法能几秒内解决问题,那为什么不用呢?多节约出几分钟,不就能多听几首银临的歌了吗。

所以,下次有个要求素数的题,尝试用埃氏筛和线性筛吧。


如果这篇博客对你有帮助,就请留下一个大拇指,最好还能点个推荐,求求您了,俺求求您了!


 

 Thank you for reading.

 

以上是关于你能告诉我一亿以内有多少对孪生素数吗?的主要内容,如果未能解决你的问题,请参考以下文章

孪生素数(用筛法求素数)

埃氏筛法(求n以内有多少个素数)

1436 孪生素数 2

1436 孪生素数 2

codevs——1436 孪生素数 2

10000以内质数的分布规律是啥?