Bootstrap

编程之路,从0开始:质数计算程序

        Hello,大家好!很高兴我们又见面了!

        给生活添点passion,开始今天的编程之路!

        今天我们来从0开始,设计一个较完美的质数计算程序。

目录

1、质数计算程序第一版

2、质数计算程序第二版

3、质数计算程序第三版

4、质数计算程序第四版

5、质数计算程序第五版

6、孪生素数


1、质数计算程序第一版

        要求,计算1到1000的所有质数并输出到屏幕上。

        首先我们来明确,什么是质数呢?

        定义:一个自然数,只有1和它本身两个因数,这样的数叫做质数(或素数)。

        那么我们很容易就想到用穷举法,把这个数除以小于这个数的所有数,如果能整除,他就不是质数,所有都不能整除,他就是质数呗?

        我们先把这串代码写出来

#include<stdio.h>
int main()
{
	int i = 0;
	int j = 0;
	for (i = 2;i <= 1000;i++)//1不是质数,所以从2开始算
	{
		for (j = 2;j < i;j++)
		{
			if (i % j == 0)//如果能整除,跳出循环
			{
				break;
			}
		}
		if (j == i)
		{
			printf("%4d", i);
		}
	}
	return 0;
}

        题目是解决了,可是这样我们要判断1000次!未免也太多了吧?我们定义变量count,检测这个程序计算了多少次运算

#include<stdio.h>
int main()
{
	int i = 0;
	int j = 0;
	int count=0;
	for (i = 2;i <= 1000;i++)
	{
		for (j = 2;j < i;j++)
		{
			count++;
			if (i % j == 0)
			{
				break;
			}
		}
		if (j == i)
		{
			printf("%4d", i);
		}
	}
	printf("\n计算次数为%d\n", count);
	return 0;
}

        可以发现,计算量是非常大的,这无疑会浪费很多时间。所以接下来我们要进一步优化这个程序。

2、质数计算程序第二版

        我们发现:所有质数都是单数。

        为什么呢?如果一个数是双数,那他肯定你呢个整除2啊!所以我们这里取消对所有双数的判断,以减少判断次数。

#include<stdio.h>
int main()
{
	int i = 0;
	int j = 0;
	int count=0;
	printf("%d", 2);//2是一个特例,所以我们先把他打印出来。
	for (i = 3;i <= 1000;i+=2)
	{
		for (j = 3;j < i;j++)
		{
			count++;
			if (i % j == 0)
			{
				break;
			}
		}
		if (j == i)
		{
			printf("%4d", i);
		}
	}
	printf("\n计算次数为%d\n", count);
	return 0;
}

        果然,计算次数少了一些!可这还不够,我们继续优化。

3、质数计算程序第三版

        我们发现,质数都无法被双数(2、4、6…)整除。

        倘若一个数是质数,那他必然不能被双数整除。倘若一个数不是质数,由于第二版的设计,我们只判断单数,单数是不能被双数整除的(双数乘任意数必然是双数,不可能是单数),既然这些数都不能被双数整除,所以我们可以把被除数的双数都去掉。

#include<stdio.h>
int main()
{
	int i = 0;
	int j = 0;
	int count=0;
	printf("%d", 2);//2是一个特例,所以我们先把他打印出来。
	for (i = 3;i <= 1000;i+=2)
	{
		for (j = 3;j < i;j+=2)
		{
			count++;
			if (i % j == 0)
			{
				break;
			}
		}
		if (j == i)
		{
			printf("%4d", i);
		}
	}
	printf("\n计算次数为%d\n", count);
	return 0;
}

很好,我们又减少了很多次运算,但是还不够,我们继续优化。

4、质数计算程序第四版

        我们发现,如果一个数不能整除3,那他也不能整除3的倍数。

        如果一个数不能整除5,那他也不能整除5的倍数。

        实际上,三的倍数也是由几个三构成的,倘若你不能整除3,又怎么能整除5呢?

        比方说现在我们判断11是不是质数,我们只用整除3 5 7 ,不用整除9,因为9是3的倍数。既然9是三的倍数,那么他可以整除3,也就是说9不是质数。我们可以再举例,判断17是不是质数,我们只需要整除3 5 7 11 13 ,不用判断15和9,因为15和9分别是5和3的倍数。而15也不是质数。所以说现在我们可以得到一个结论:

        质数无法被小于自己的质数整除。

现在我们对程序进行优化:

#include<stdio.h>
int main()
{
	int i = 0;
	int j = 0;
	int count=0;
	int arr[500] = { 0 };
	int ptr = 0;
	arr[ptr++] = 2;
	arr[ptr++] = 3;//将第一位被除质数放入数组中
	for (i = 5;i <= 1000;i+=2)
	{
		for (j = 1;j < ptr;j++)//从1开始,去除了2这个双数特例
		{
			count++;
			if (i % arr[j] == 0)
			{
				break;
			}
		}
		if (j == ptr)
		{
			arr[ptr++] = i;//将质数存到数组中
		}
	}
	for (i = 0;i < ptr;i++)//循环打印数组
	{
		printf("%4d", arr[i]);
	}
	printf("\n计算次数为%d\n", count);
	return 0;
}

很好,又减少了很多运算,但是还能继续优化。

5、质数计算程序第五版

        现在我们来进行最关键的一步。

        我们发现,如果检测15是不是质数,由于3乘5等于15,那么我们检测完三就不需要检测十五了。

        所以,我们只需要整除 被检测数的开方 之前的质数就可以了。

        现在我们实现该代码

#include<stdio.h>
int main()
{
	int i = 0;
	int j = 0;
	int count=0;
	int arr[500] = { 0 };
	int ptr = 0;
	arr[ptr++] = 2;
	arr[ptr++] = 3;//将第一位被除质数放入数组中
	for (i = 5;i <= 1000;i+=2)
	{
		int flag = 1;//标记
		for (j = 1;arr[j]*arr[j]<=i;j++)//从1开始,去除了2这个双数特例
		{
			count++;
			if (i % arr[j] == 0)
			{
				flag = 0;
				break;
			}
		}
		if (flag)
		{
			arr[ptr++] = i;//将质数存到数组中
		}
	}
	for (i = 0;i < ptr;i++)//循环打印数组
	{
		printf("%4d", arr[i]);
	}
	printf("\n计算次数为%d\n", count);
	return 0;
}

        因为我们的被检测数小于1000,所以不会产生溢出的情况。如果被检测数非常的的话,需要写成这种方式来预防溢出

#include<stdio.h>
#include<math.h>
int main()
{
	int i = 0;
	int j = 0;
	int count=0;
	int arr[500] = { 0 };
	int ptr = 0;
	arr[ptr++] = 2;
	arr[ptr++] = 3;//将第一位被除质数放入数组中
	for (i = 5;i <= 1000;i+=2)
	{
		int flag = 1;//标记
		for (j = 1;arr[j]<=floor(sqrt(i)+0.5);j++)//从1开始,去除了2这个双数特例
		{
			count++;
			if (i % arr[j] == 0)
			{
				flag = 0;
				break;
			}
		}
		if (flag)
		{
			arr[ptr++] = i;//将质数存到数组中
		}
	}
	for (i = 0;i < ptr;i++)//循环打印数组
	{
		printf("%4d", arr[i]);
	}
	printf("\n计算次数为%d\n", count);
	return 0;
}

        由于sqrt开方之后会向下舍入,造成误差,所以我们先给他加上0.5,再floor向下舍入,相当于四舍五入了。这样做可以防止数据溢出(但我们这个程序其实没有这个顾虑),并且尽可能增加准确度,但是会极少量增加计算量,建议根据情况使用。

        好了,到目前为止我们的质数计算程序就设计完了,现在我们应用一下。

6、孪生素数

        如果n和n+2都是素数(质数),则称他们为孪生素数。输入m,输出两个数均不超过m的最大孪生素数。

        我们先将质数判断程序封装成函数

#include<stdio.h>
#include<math.h>
int test(int arr[500],int i)
{
	int j = 0;
	if (i == 0)
	{
		return 0;
	}
	for (j = 1;arr[j] <= floor(sqrt(i) + 0.5);j++)//从1开始,去除了2这个双数特例
	{
		if (i % arr[j] == 0)
		{	
			return 0;
		}
	}
	return 1;
}
int main()
{
	int i = 0;
	int a = 0;
	int count=0;
	int arr[500] = { 0 };
	int ptr = 0;
	arr[ptr++] = 2;
	arr[ptr++] = 3;//将第一位被除质数放入数组中
	for (i = 5;i <= 1000;i+=2)
	{
		a=test(arr,i);
		if (a == 1)
		{
			arr[ptr++] = i;
		}
	}
	for (i = 0;i < ptr;i++)//循环打印数组
	{
		printf("%4d", arr[i]);
	}
	return 0;
}

        现在我们稍作改造,实现代码:

#include<stdio.h>
#include<math.h>
int test(int arr[500],int i)
{
	int j = 0;
	if (i == 0)
	{
		return 0;
	}
	for (j =1;arr[j] <= floor(sqrt(i) + 0.5);j++)//从1开始,去除了2这个双数特例
	{
		if (i % arr[j] == 0)
		{	
			return 0;
		}
	}
	if (i % 2 == 1)//如果是双数,返回0
	{
		return 1;
	}
	else
		return 0;
}
int main()
{
	int i = 0;
	int a = 0;
	int m=0;
	int arr[500] = { 0 };
	int ptr = 0;
	printf("请输入数字:");
	scanf_s("%d", &m);
	if (m < 5)
	{
		printf("不存在最大孪生素数。\n");
	}

	arr[ptr++] = 2;
	arr[ptr++] = 3;//将第一位被除质数放入数组中
	for (i = 5;i<=m;i+=2)
	{
		a=test(arr,i);
		if (a == 1)
		{
			arr[ptr++] = i;
		}
	}
	for (i = m - 2;i > 2;i--)
	{
		if (test(arr, i) == 1 && test(arr, i + 2) == 1)
		{
			printf("%d %d", i, i + 2);
			break;
		}
	}
	
	return 0;
}

        运行结果示例:

        好了,今天的内容就分享到这,期待我们的下次见面!

;