素数

文章目录

定义

一个数只能被1和它本身整除,那么这个数就是素数。

问题

求区间之间的素数的个数?如[2, n]中有多少个素数?

解法

  • 初学者的方法:

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

    这样的时间复杂度是 O(n^2),显然不够高效。其实我们只需要稍微修改函数isPrime中的for循环

    for(int i = 2; i * i < n; i++)
    		if(n % i == 0)	return false;
    eg:
    16 = 2 * 8;
    16 = 4 * 4;
    16 = 8 * 2; //这里就是重复计算的
    

    所以说在[2, sqrt(n)]没有找到可整除的因子,在[sqrt(n), n]区间肯定也没有。这样时间复杂度就一下子缩小到了O(sqrt(n))。

  • 高效地解决方法:见下图

    img

    我们可以看到,上面的方法其实有很多的重复,或者加做啰嗦,举个栗子:

    2是素数,那么:2 × 2 = 4, 2 × 3 = 6, 2 × 4 = 8…肯定均不为素数;

    3是素数,那么:3 × 2 = 6, 3 × 3 = 9, 3 × 4 = 12…肯定均不为素数。

    int countPrimes2(int n){
    	bool *isPrime = new bool[n + 1];
    	fill(isPrime, isPrime + 2, true) ;
    	for(int i = 2; i < n; i++){
    		if(isPrime[i]){
    			for(int j = i * 2; j < n; j += i)
    				isPrime[j] = false; 
    		}
    	}
    	int count = 0;
    	for(int i = 2; i < n; i++)
    		if(isPrime[i])	count ++;
        delete []isPrime;
    	return count;
    }
    

    这个代码还有两处值得优化的地方,第一个就是和上面的第一种解法的原因一样,第一个for循环的地方:

    for(int i = 2; i * i< n; i++){
    	...
    }
    

    第二处就是第二个for循环的地方,例如

    i = 2时:4,6,8,10,12,14 被标注为非素数;

    i = 3时:6,9,12,15,18,21被标注为非素数;

    i = 4时:6,8,12,16,20,24被标注为非素数;

    从中可以看出,当i = 4 时,就已经计算了很多重复的计算量,所以我们可以稍微优化一下,如:

    for(int i = 2; i < n; i++){
    		if(isPrime[i]){
    			for(int j = i * i; j < n; j += i)
    				isPrime[j] = false; 
    		}
    	}
    

    这种方法有个名字,叫做sieve of eratosthenes最终的完整代码如下:

    int countPrimes2(int n){
    	bool *isPrime = new bool[n + 1];
    	fill(isPrime, isPrime + 2, true) ;
    	for(int i = 2; i < n; i++){
    		if(isPrime[i]){
    			for(int j = i * 2; j < n; j += i)
    				isPrime[j] = false; 
    		}
    	}
    	int count = 0;
    	for(int i = 2; i < n; i++)
    		if(isPrime[i])	count ++;
    	delete []isPrime;
    	return count;
    }
    

    不过这种方法也有它的缺陷,就是对空间的需求量比较大。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值