素数判断

试除法

根据效率的不同,试除法又分为几个小的类型:
第一种:
素数就是除了1和它本身,不能被其他数整除的数,试除法就是将除了1和其本身的数之外所有的数遍历一遍,如果有一个数能够整除,就不是素数。

int main()
{
	int i = 0;
	
	for (i = 100; i <= 200; i++)
	{
		int j = 0;
		for (j = 2; j < i; j++)
		{
			if (0 == i%j)
				break;
		}
		if (j == i)
		{
			printf("%d ", i);
		}
	}
	return 0;
}

第二种:
如果一个数不是素数的话,其因数一定是小于等于这个数的一半的,因此只需要遍历一半的数就可以。

int main()
{
	int i = 0;
	for (i = 100; i <= 200; i++)
	{
		int j = 0;
		for (j = 2; j <= (i / 2); j++)
		{
			if (0 == i%j)
				break;
		}
		if (j > (i / 2))
			printf("%d ", i);
		
		
	}
	return 0;
}

第三种:
质数不可能是偶数,只要是偶数就可以被2整除,那么就可以想到,只需要判断奇数是不是质数就可以了。再根据类型2,遍历一半即可。

int main()
{
	int i = 0;
	for (i = 101; i <= 200; i+=2)
	{
		int j = 0;
		for (j = 2; j <= (i / 2); j++)
		{
			if (0 == i%j)
				break;
		}
		if (j > (i / 2) )
			printf("%d ", i);


	}
	return 0;

第四种:
如果细心观察就会发现,如果不是素数的话,因数都是成对出现的,拿100来说:1和100、2和50、4和25、5和20、10和10,这些因数里,一个大于100的开平方,一个小于100的开平方,小的数和大的数是成对的,出现一个就不需要另一个了,很明显用小于开平方的数效率更高。因此只需要遍历到一个数的开平方即可。再利用第三种的规律,只判断奇数即可。这样子一下就会快很多。

#include <math.h>
int main()
{
	int i = 0;
	for (i = 101; i <= 200; i += 2)
	{
		int j = 0;
		for (j = 2; j <= sqrt(i); j++)
		{
			if (0 == i%j)
				break;
		}
		if (j > sqrt(i) )
			printf("%d ", i);


	}
	return 0;
}

第五种:
按照第四种的算法,会发现如果一个数能被除了2之外的偶数整除,那么一定会被2整除;反之如果不能被除了2之外的偶数整除,那么肯定不会被2整除。想到这里,我们发现在遍历的过程中,只需要用2试一下就不需要再去遍历其他的偶数。
那么,先尝试2,然后对3到开平方之间的奇数进行试除就是最优的方法吗?
举一个例子大家就明白了,101的开平方是10,除了2之外,奇数为3、5、7、9,发现3和9是有关系的,不能被9整除的,肯定不会被3整除,因此还要排除掉这些有关系的数。我们发现,只需要试除开平方之前的质数就可以了,因为这些数既是奇数、又不可能和其他的数有关系,这就能够省去一些时间。
但是这种境界的判断素数,需要能够将已经计算出来的质数保存起来,这里我想到的就是用数组进行保存,但是这就涉及到变长数组的问题,因为每一个数开平方之前的质数的数量都是变化的,数组的长度也就是变化的。我的编译器不支持变长数组,也可以用到给宏传参的知识,在后面我会完善这一块的代码。

筛选法

筛选法的原理如下:
2是最小的质数,先把2的倍数去掉,剩下的数里面,最小的是3,那么3就是质数。
然后在剩下的数里面,再把3的倍数去掉,剩下的数里面,最小的是5,那么5就是质数
以此类推,就可以把一个范围内所有的合数去掉,只留下质数。

用程序实现的时候,先确定一个范围内有多少个自然数,定义一个该长度的数组用来储存,先全部默认为0。
2是最小的质数,然后去掉2的倍数,将这些元素在数组中赋值为1。
之后的下一个不是1的数是3,3是质数,然后去掉3的倍数,将这些元素置1.
下一个不是1的数是5,5是质数,再去掉5的倍数。
以此类推,得到一个范围内全部的质数。

筛选法这部分在网上也看到过一些帖子,做法如下:

for (i = 0; i < 100; ++i)
{
	j = i - 1;//空过0、1、2
	while (j >1)
	{
		if (arr[i]% j == o)
		arr[i] = 0;
		j = j - 1;
	}
}

我觉得这样子并没体现筛选法的精髓,这样数越大循环的次数越多,并没有提高效率。

我的方法就是定义一个全0的数组,先将2的倍数的元素改为1,然后将3的倍数改为1,以此类推,并且在循环的开头判断下一个数是0还是1,如果是0,这个数就是素数,然后去掉这个数字的倍数,如果是1,说明它不是素数,不需要往下进行,这样子的效率会提高很多。

//筛选法
int main()
{
	//假设求100-200之间的素数
	int arr[101] = { 0 };
	int i = 0;
	int j = 0;
	arr[0] = 1;
	arr[1] = 1;
	for (i = 2; i <= 100; i++)
	{
		if (arr[i] == 1)
			continue;
		//因为2是公认最小的质数,因此跳过2,从3开始
		for (int j = i * 2; j <= 100; j+=i)
		{
			if (arr[j] == 1)
				continue;
			arr[j] = 1;

		}
		
	}
	for (j = 1; j <= 100; j++)
	{
		if (arr[j] == 0)
			printf("%d ", j);
	}
	return 0;
}

这个地方还有改进的地方:
1.在每次将倍数置1,也就是去掉的时候,肯定会有很多重复的数,我用if进行判断,但是这样其实还是会将整个循环走完,我的想法是将倍数直接从数组中去掉,也就是将不是素数的数从数组中删除,但是我的知识有限,或者不够了解,仅作为一个猜想。
2.定义一个int数组储存0和1是比较浪费空间的,在C++中可以用布尔类型来创建数组,因为布尔类型只有0和1,占用空间较小。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值