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

原创 2015年07月07日 19:01:27

题目描述

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毫秒解决,我看了一会儿没看懂,就没继续研究。。。我承认我还是很懒的。。。


版权声明:本文为博主原创文章,未经博主允许不得转载。

素数的个数统计

1.从C语言的语法设计来说是不支持的,早期的C编译器未考虑过函数重载这一功能,所以就会有那么多类似的函数abs,labs,fabs等等(每种类型都要考虑一个不同的函数名)。 2.C语言标准就规定不允...

统计给定整数M和N区间内素数的个数

第5题 【描述】 本题要求统计给定整数M和N区间内素数的个数并对它们求和。 要求定义和调用函数:int isPrime(int n),如果n是素数,该函数返回1,否则返回0。 【输入】 输入...

4-3. 统计素数并求和

本题要求统计给定整数M和N区间内素数的个数并对它们求和。 输入格式: 输入在一行中给出2个正整数M和N(1 输出格式: 在一行中顺序输出M和N区间内素数的个数以及它们的和,数字...

NowCoder猜想--牛客网题

NowCoder猜想 反正没人看,随便写写,话说这还是我的第一篇博客。。。 时间限制:1秒空间限制:32768K通过比例:4.49% 参与人数:729 ...

AOJ-AHU-OJ-7 Redraiment猜想

Redraiment猜想 Time Limit: 1000 ms   Case Time Limit: 1000 ms   Memory Limit: 64 MB   Description redr...

线性代数(三十三) : 凯莱-哈密顿定理

凯莱-哈密顿定理由两位数学家的名字命名,该定理有利于寻找标准若尔当形。 1 凯莱哈密顿定理 矩阵A满足其自身的特征多项式: 证明: 该定理的证明需要分以下两种情况: (i)  A无相同特征值 此时...

java位运算符 : 与(&)、非(~)、或(|)、异或(^)

[转] 1.位与运算符(&) 运算规则:两个数都转为二进制,然后从高位开始比较,如果两个数都为1则为1,否则为0。 比如:129&128. 129转换成二进制就是10000001,128转...
  • anincl
  • anincl
  • 2017年11月30日 17:53
  • 16

超高速计算n以内素数个数(百亿内3毫秒解决)

判断n以内素数个数有很多算法,最简单的是循环直接判断,这个效率不用说,n稍大就不行了。最流行的是筛选法,原理就是定义一个素数标志位表,初始为1,遇到一个数如果对应标志位为1判断这个数是不是素数,是将该...

求N以内素数个数--粗暴方式及筛选法

素数,是除了1和它本身之外不再被其他的除数整除。 使用程序求N以内素数个数问题,在各种语言的基础教程中都会讲到。 一般而言,求取素数可以使用粗暴的从2开始遍历到自己,每次拿自己整除这些遍历的数,若可以...

【ZOJ 1562和 BZOJ 1053】【反素数】【求n以内的因子最多的那个数(即不超过n的最大反素数)】

传送门1:http://acm.zju.edu.cn/onlinejudge/showProblem.do?problemId=1562  传送门2:http://www.lydsy.com/Judg...
内容举报
返回顶部
收藏助手
不良信息举报
您举报文章:NowCoder猜想---统计n以内的素数个数
举报原因:
原因补充:

(最多只允许输入30个字)