leetcode-204-Count Primes

题目描述:

Count the number of prime numbers less than a non-negative number, n.

 

要完成的函数:

int countPrimes(int n) 

 

说明:

1、题目看上去非常简单和熟悉。给定一个非负数n,要求返回小于n的所有素数的个数。

2、处理一下边界条件,n<=2时返回0,n=3时返回1,n=4时返回2。

3、传统方法:

对于小于n的每个数i,判断i是不是素数。判断方法是对于每个大于等于2且小于等于i/2的数,确定i能否整除这个数。

双重循环,暴力解法。然后在这道题中超时了。

代码如下:

bool prime(int i)
    {
        for(int j=2;j<=i/2;j++)//小循环
        {
            if(i%j==0)
                return false;
        }
        return true;
    }
    int countPrimes(int n) {
        int count=0;
        if(n<=2)
            return count;
        else if(n==3)
            return 1;
        else if(n==4)
            return 2;
        count=2;
        for(int i=4;i<n;i++)//大循环
        {
            if(prime(i))
                count++;
        }
        return count;
    }

 

4、改进1:

我们尝试做一些改进,比如在大循环中,能不能只判断奇数,毕竟偶数都不是素数,没有必要判断,这样可以省很多时间。

此外,在小循环中,能不能控制j为奇数,毕竟大循环中的要判断的奇数,只会由另外两个奇数相乘而得到。

还有,我们可以把小循环中的判断条件:i/2,改成sqrt(i)。这个也能省不少时间。

我们来试一下:

    bool prime(int i)
    {
        for(int j=3;j<=sqrt(i);j+=2)//j每次都+2
        {
            if(i%j==0)
                return false;
        }
        return true;
    }
    int countPrimes(int n) {
        if(n<=2)//这次要判断的边界条件比较多,相信看完一整个代码的你会理解的
            return 0;
        else if(n==3)
            return 1;
        else if(n==4||n==5)
            return 2;
        else if(n==6||n==7)
            return 3;
        else if(n==8)
            return 4;
        int count=4;
        for(int i=9;i<=n;i+=2)//从i=9开始判断,只对奇数进行判断
        {
            if(prime(i))
                count++;
            if(i==n&&prime(n))//如果n是一个奇数和素数,那么count要减去1;如果n是奇数和非素数,那么不用减
                  //去1,因为上一行代码在最后没有执行到。 count
--;    //如果n是一个偶数,那更加不用减去1。 } return count; }

这次代码通过测试,beats 7.36% of cpp submissions,实测452ms。

 

5、改进2:

我们能否再做一些改进?

上述代码浪费时间的地方在于:

比如我们要判断19*31=589是不是素数,那么我们要对589%9做出判断,对589%11做出判断,对589%13做出判断,对589%15做出判断,对589%19做出判断。

然后我们对于下一个数19*41=779判断是否素数,要对779%9做出判断,对779%11做出判断,对779%13做出判断,对779%15做出判断,对779%19做出判断。

我们浪费了很多时间在不停地判断上面。我们可不可以用素数相乘的方式,直接生成一些合数,然后不断地筛掉它们。这样可以避免花费大量时间在判断上面。

代码如下:

int countPrimes(int n) 
{
    if (n<=2) 
        return 0;
    vector<bool> prime(n, false);//定义一个长度为n,初始为false的bool型vector容器
    int sum = 1;
    int upper = sqrt(n);
    for (int i=3; i<n; i+=2) //对每个奇数进行处理
    {
        if (!prime[i]) //初始i=3,是一个质数,进入后续处理。之后每次都判断是不是质数,如果是就进行处理。
        {
            sum++;
            if (i>upper) 
                continue;
            for (int j=i*i; j<n; j+=2*i) //比如3*3=9,9就是一个合数。然后9+3*2=15=i*i+2i,也是一个合
       {                  //数。后续不断循环处理。这里最好是处理成j+=2*i,而不是j+=i,                  //思考一下原因?评论区有答案~这个细节可以省一半时间。 prime[j]
= true; } } } return sum; }

最终实测16ms,beats 97% of cpp submissions。

 

posted @ 2018-04-13 15:18  chenjx85  阅读(795)  评论(1编辑  收藏  举报