NowCoder猜想---统计n以内的素数个数

来源:互联网 发布:少年班人物原型知乎 编辑:程序博客网 时间:2024/05/16 15:17

题目描述

nowcoder在家极度无聊,于是找了张纸开始统计素数的个数。
设函数f(n)返回从1-n之间素数的个数。
nowcoder 发现:
f(1)   = 0
f(10)  = 4
f(100) = 25
...
满足g(m) = 17 * m^2 / 3 - 22 * m / 3 + 5 / 3
其中m为n的位数。
他很激动,是不是自己发现了素数分布的规律了!
请你设计一个程序,求出f(n),来验证nowcoder是不是正确的,也许还可以得诺贝尔奖呢。^_^

输入描述:
输入包括多组数据。每组数据仅有一个整数n (1≤n≤10000000)。


输出描述:
对于每组数据输入,输出一行,为1->n(包括n)之间的素数的个数。

输入例子:
110651000
输出例子:
041825

--------------------------我是华丽的分割线-------------------------------------------------
这道题其实很简单,会判断素数,会统计个数,再写个循环就可以啦。
所以第一种,也是最容易想到的做法就是,循环从1< i <= n, 循环中加上判断i是否为素数,是则计数+1,否则不加
代码实现如下:
这道题其实很简单,会判断素数,会统计个数,再写个循环就可以啦。
所以第一种,也是最容易想到的做法就是,循环从1< i <= n, 循环中加上判断i是否为素数,是则计数+1,否则不加
代码实现如下:
</pre><pre name="code" class="cpp">#include <stdio.h>#include <stdlib.h>#include <math.h>#include <time.h>int is_prime(int n){    int i = 2;    if (n < 2)    {        return 0;    }    while (i <= sqrt(n))    {        if (n%i == 0)        {            return 0;        }        i++;    }    return 1;}int prime(int n){    int cnt = 0;    int i = 0;    for (i = 2; i <= n; i++)// += 2)    {        if(is_prime(i))        {            cnt++;        }    }    return cnt;}int main(){    int n = 0;    int cnt = 0;    clock_t st, en;    while (1)    {        scanf("%d", &n);        if (n == 0)        {            break;        }        cnt = prime(n);        printf("%d\n", cnt);    }    return 0;}

以上就是第一种,最直接的想法实现的代码。根据实际测试,这个代码是可以运行的。但是,跑程序是讲究效率的,OJ是有时间限制的,
当n=1000 000 时,这个程序的速度就很慢了,更不用提n=10 000 000的时候了。
因此我们就需要对这个程序进行改进。首先我想到的是,既然是素数,那么偶数肯定就不是素数啦,这是很明显的。但是上面的程序也对偶数进行了判断,因此这里的时间浪费了不少(毕竟偶数个数占了一半呢!)。然后我又想,偶数是2的倍数,那么3、5、7、11的倍数也不可能是素数,这样素数判断的次数就会减少不少了。
int is_prime(int n){    int i = 2;    if (n < 2)    {        return 0;    }    if (n == 2 || n == 3 || n == 5 || n == 7 || n == 11)        return 1;    if (n%2 == 0 || n%3 == 0 || n%5 == 0 || n%7 == 0 || n%11 == 0)        return 0;    while (i <= sqrt(n))    {        if (n%i == 0)        {            return 0;        }        i++;    }    return 1;}int prime(int n){    int cnt = 0;    int i = 0;    if (n >= 2)        cnt++;    for (i = 3; i <= n; i += 2)    {        if(is_prime(i))        {            cnt++;        }    }    return cnt;}

prime()和is_prime()这个两个函数修改之后,程序的运行速度提升并没有想象的多,因此还需继续想办法。。。。
然后我发现我想不动了 /(ㄒoㄒ)/~~于是就各种google,各种百度看看别人怎么做,然后就通过这篇博客http://blog.csdn.net/code_pang/article/details/7880245发现了一个有趣的结论。
对于大于等于5的素数素数只可能是6n-1或者6n+1,因此,这样就可以加快素数的判断了。
于是我写了一个新的函数is_prime1()
int is_prime1(int n){    int i = 0;    if (n == 2 || n == 3 || n == 5 || n == 7 || n == 11)    {        return 1;    }    if (n%3 == 0 || n%5 == 0 || n%7 == 0 || n%11 == 0)    {        return 0;    }    if (n%6 != 1 && n%6 != 5)    {        return 0;    }    for (i = 5; i*i <= n; i+= 6)    {        if (n%i == 0 || n%(i+2) == 0)        {            return 0;        }    }    return 1;}

根据测试结果可以知道,当n=10^6时,运行结果瞬间就出来了,但是当n=10^7时,结果还是需要数秒钟才能出来。因此还是没有通过OJ/(ㄒoㄒ)/~~
因此还是必须继续寻找更快的方式。
此时,删选法出现了,百度百科有相关的解释http://baike.baidu.com/view/2722688.htm,有兴趣的可以去看看。
由于某博客博主的代码注释和详细,因此我就直接复制过来,但是由于写这篇博客的时候,我已经找不到这个代码片作者的那个博客了,无法提供链接。如果此作者看到了,可以给我留言,要求我删除或者注明改代码的引用都是可以的:-D
int CompositeNumFilterV2(int n){    if(n==1)    {        return 0;    }    int i, j;    // 素数数量统计    int count = 0;    // 分配素数标记空间,明白+1原因了吧,因为浪费了一个flag[0]    bool* flag = new bool[n+1]; //这个要是用int* flag=new int[n+1],就会溢出,因为int占4bit,则4*1000000000=4M左右的内存,而用bool或者char,则少得多    // 初始化素数标记,要高效点咯    flag[2]=1;    // 注意是i<n不是上例中的i<=n了    for (i=3; i<n; i++)    {        //flag[i]=(i%2!=0)?1:0;//偶数直接值0,奇数值1,2特殊处理,但是这个有判断操作,效率肯定没有直接赋值快,所以用下面的        flag[i++]=1;        flag[i]=0;    }    // n为奇数    if (n%2 != 0)    {        flag[n] = 1;    }    // 从3开始filter,因为2的倍数早在初始化时代就干掉了    for (i=3; i <= sqrt((double)n); i++)    {    // i是合数,请歇着吧,因为您的工作早有您的质因子代劳了        if (0 == flag[i]) continue;    // 从i的平方倍开始过滤,而不是从前面的已经被其他素数筛掉了,另外变乘法为加法,加法效率比乘法快        for (j=i*i; j <= n; j+=i)        {            flag[j] = 0;        }    }    // 统计素数个数    for (i=2; i<=n; i++)    {        if (flag[i]) count++;    }    // 释放内存,别忘了传说中的内存泄漏    delete[] flag;    return count;}

这个函数放到程序中运行,可以在1秒中之内计算完10^7以内素数的个数。速度还是满足要求的。成功通过了OJ测试。O(∩_∩)O~
其实也可以看到此函数在实现素数统计的过程中其算法的复杂度是O(n)的,而之前我的做法是O(n^2)的。所以当n比较大的时候,速度肯定会慢不少的。
最后,在搜索这个编程题更好解法的过程中,我发现有位网友http://www.2cto.com/kf/201303/194987.html  提供了一个算法,号称百亿内3毫秒解决,我看了一会儿没看懂,就没继续研究。。。我承认我还是很懒的。。。


1 0
原创粉丝点击