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

题目描述

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)之间的素数的个数。

输入例子:
1
10
65
100
0
输出例子:
0
4
18
25

--------------------------我是华丽的分割线-------------------------------------------------
这道题其实很简单,会判断素数,会统计个数,再写个循环就可以啦。
所以第一种,也是最容易想到的做法就是,循环从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毫秒解决 ,我看了一会儿没看懂,就没继续研究。。。我承认我还是很懒的。。。


  • 2
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 3
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值