素数问题

素数,又称质数,是一个大于一的自然数,除了1和它本身外,不能被其他的自然数整除的数被称为质数,否则成为合数

1不属于质数也不属于合数

目录

方法一:

 方法二:

方法三:



下面给出一道例题并列出了三种实现算法,以此介绍关于素数的算法,其中第三种时间复杂度最小

 


方法一:

单独判断1,然后从2到n - 1, 判断是否存在能被n整除的数,既

(n%i == 0, 2 <= i <= n - 1),如果有就不是素数,否则为素数。

代码中附上程序运行时间,一1000,000为例,数字太小的话无发凸显代码优势。以此判断程序的时间复杂度。由于机器原因时间复杂度的比较不是太明显。但第三种算法要远远优于其他两种方法。

代码:

#include<cstdio>
#include<ctime>
using namespace std;
int main()
{
	int n,i,j;
	while(~scanf("%d",&n))//输入次数小于或等于30 
	{
		int sum=1;
		if(n==1)
		{
			sum=0;
		}
		for(i=3;i<=n;i+=2)//由于偶数除了2之外倒不是素数所以只需要判定奇数即可 
		{
			for(j=2;j<i;j++)
			{
				if(i%j==0)
				{
					break;
				}
			}
			if(j==i)//当j==i时此回合判定结束 
			{
				sum++;
			}
		}
		printf("Time used = %0.2f s\n",(double)clock()/CLOCKS_PER_SEC);//获取程序运行的时间	
		printf("%d\n",sum);	
		
	}	
	return 0;
}

 运行结果:

1000000数量级未带动

100000数量级运行时间在6秒左右明显处劣势


 方法二:

方法二其实都是对上述方法进行优化,可以很好地降低时间复杂度。利用以下结论:对正整数n,如果用2到n(根号n之间的所有整数去除,均无法整除,则n为质数。

代码:

#include<cstdio>
#include<cmath>
#include<ctime>
using namespace std;
int main()
{
	int n,i,j;
	int temp=1;
	while(~scanf("%d",&n))//输入次数小于或等于30 
	{
	  	int sum = 1;
	  	if(n==1)
	  	{
	  		sum=0;
		}
	    for( i = 3; i <= n; i+=2)
	    {//因为偶数除了2之外都不是素数(质数),所以只需判断奇数,从3开始每次+2
	        for(j = 2; j <= (int)sqrt(i); j++)//利用上述结论判断
	            {
	            	if(i%j == 0)break;	
				}				
	        if(j > (int)sqrt(i))
	        {
	        	sum++;
			}
	            
	    }

		printf("Time used = %0.2f s\n",(double)clock()/CLOCKS_PER_SEC);//获取程序运行时间	
		printf("%d\n",sum);	
	
	}	
	return 0;
}

运行结果:


 

方法三:

方法三使用到孪生素数的一些特点和结论

孪生素数:孪生素数指的是间隔为 2 的相邻素数。

1.当n >= 6, n - 1 和 n + 1 为孪生素数,那么n 一定是6的倍数。

Proof :

∵ n - 1, n + 1是素数,也即 n - 1 和 n + 1是 奇数

∴ n 是 偶数,n 是 2 的倍数。

设 n 不是 3 的倍数,即 n = 3k + 1 或 n = 3k + 2。

(i)当 n = 3k + 1 时,那么 n - 1 = 3k,已经与 n - 1 是素数矛盾。

(ii)当 n = 3k + 2 时, 那么 n +1=3(k + 1),已经与 n + 1是素数矛盾。

综上所述,n 是 3 的倍数。

∵n既是2的倍数,又是3的倍数

∴n 是6的倍数。

 

推论1:当 x >= 1, (6x - 1)或 (6x + 1)不是素数时,它们的质因子不包括2和3的倍数,因为2(3x) - 1, 3(2x) - 1,2(3x) + 1, 3(2x) + 1。

 

2.素数分布规律:当n >= 5时,如果n为素数,那么n % 6 = 1 || n % 6 = 5,即n一定出现在6x(x≥1)两侧。

(就是说大于等于5的素数一定是分布在6倍数的左右两侧,但在6倍数左右两侧的数不一定是素数)

Proof

可以把6x附近的数用以下方式表示:

……(6x - 1), 6x, 6x+1, 2(3x+1), 3(2x+1), 2(3x +2), 6x + 5, 6(x+1)……

不在6x两侧的数为: 2(3x+1), 3(2x+1), 2(3x +2),它们不是素数,所以素数出现在6x的两侧。

 

有了以上的理论基础,我们可以对方法2进一步地优化,首先不在6x左右的数2,3单独处理,接下来只要判断 6x两侧的数是否为素数。因为合数总是可以写成素数的乘积,那么我们直接用n去除以质数就可以达到很好地优化目的。而质数一定是 6x 两侧的数(推论一已证明了当 n >= 5 时,n不是素数时,n 不含质因子2,3) , 6x 两侧的数是大于素数的集合,因此可以用n 除以 6x 两侧的数即if(n % i == 0 || n % (i +2) == 0)时,不是素数。

代码:

#include<cstdio>
#include<cmath>
#include<ctime>
using namespace std;
int main()
{
	int n,i,j;
	while(~scanf("%d",&n))//输入次数小于或等于30 
	{
    int sum = 1;//已经将2单独处理
    if(i == 1)//3单独处理
    {
        sum=0;
    } 
    
    int flag = 0;
    for(int i = 3; i <= n; i+=2)
    {
        if(i == 3)//3单独处理
        {
            sum++;
        }  
        if(i % 6 != 1 && i % 6 !=5)
        {
            continue;//不是6x两侧的数不是素数
        }

        /*
			上面的判定根据孪生素数尽可能的减小了需判断数的数量,极大地节省了程序运行的时间 
			经过筛选,将筛选后剩余的数,通过普通方法在进行判断 
		*/	
        for( j = 5; j <= (int)sqrt(i); j+=6)//对6x两侧的数进行判断
        {
            if(i%j == 0 || i%(j + 2) ==0)
            {
                flag = 1;
                break;
            }
        }

            if(flag == 1)
            {//如果判定不为素数,flag被赋值为1;i+2也不为素数 ,因此判断flag是否等于1进行拦截 
                flag = 0;
                continue;
            }
        sum++;
    }
		printf("Time used = %0.2f s\n",(double)clock()/CLOCKS_PER_SEC);//获取程序运行的时间	
		printf("%d\n",sum);	
			
	}	
	return 0;
}

运行结果:


  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值