【素数求解的五大境界——C语言】

💖 技术宅,拯救世界!

🎁 对读者的话:相信奇迹的人本身和奇迹一样伟大

在这里插入图片描述

生命中最快乐的事情是拼搏,而非成功,生命中最痛苦的是懒散,而非失败。大家好,这里是ecember,学了比较久C语言的同学,肯定已经把素数啃成老油条了,莫慌,莫慌,今天ecmber来带大家了解一下素数求解的五大境界。(以下结果均在vs2022中编译哦)


🌹感谢大家的点赞关注 🌹,如果有需要可以看我主页专栏哟💖


⚡1.素数求解的五大境界

这里我们统一测试1-100的数据。目的是打印1-100之间的素数以及输出素数个数。

🌠1.1 试除法——境界一

首先,何为素数呢?素数即为在正整数范围内,大于1且只能被1和自身整除的数,我们这里要打印素数以及输出素数的个数,那么我们在外部想先给一个1-100for循环来判断素数,然后再打印,最后再计数,重点是这个判断素数部分,我们可以根据素数定义 (整除),我们试想——不妨用1-100的数一个一个除以判断数,如果之间有任何一次余数为0,则该数不是素数,这样,我们在内层在嵌套一个for循环判断是不是就搞定啦。(注意1不是素数,2是素数

//境界一
#include <stdio.h>
int main()
{
	int i = 0, j = 0;
	int count1 = 0, count2 = 0;

	printf("1 —— 100之间素数有:\n");
	for (i = 1; i <= 100; i++)
	{
		for (j = 2; j < i; j++)
		{
			count1++;
			if (i % j == 0)
			{
				break;
			}
		}
		if (j >= i && i != 1)//j >= i说明1-100除完了也没有让i % j == 0的数即为素数
		{
			printf("%d ", i);
			count2++;
		}
	}

	printf("\n**************\n");
	printf("循环次数:%d\n", count1);
	printf("素数个数:%d\n", count2);
	printf("**************\n");

	return 0;
}

在这里插入图片描述

我们这里在最深层是添加一个监视变量count1(循环次数),可以看出,我们打印1-100之间的素数时,编译器总共要循环个1133次,试想如果我们将数扩展到一千或者一万,循环次数是个多么庞大的数字,所以我们需要减少循环次数,缓解编译器压力,也就是所谓的算法优化

🌠1.2 试除法plus——境界二

我们试想大于2的偶数是不是都能被2整除,此时它就不符合素数的特征了,故我们可以在判断数上做文章——将其全改为奇数

//境界二
#include <stdio.h>
int main()
{
	int i = 0, j = 0;
	int count1 = 0, count2 = 0;

	printf("1 —— 100之间素数有:\n");
	printf("%d ", 2);
	count2++;
	for (i = 3; i <= 100; i += 2)
	{
		for (j = 2; j < i; j++)
		{
			count1++;
			if (i % j == 0)
			{
				break;
			}
		}
		if (j >= i)
		{
			printf("%d ", i);
			count2++;
		}
	}

	printf("\n**************\n");
	printf("循环次数:%d\n", count1);
	printf("素数个数:%d\n", count2);
	printf("**************\n");

	return 0;
}

在这里插入图片描述

这里我们将判断数改为奇数后,循环次数果然少了一些,但依然还是逃离不了1000这个数量级,我们进一步优化。

🌠1.3 试除法plusplus——境界三

除此之外,我们还能在哪里下文章呢?既然要降低循环次数,那么我们盯紧循环体,难道除数一定要一直除直到等于自身才算素数吗,显然不是,我们举个最简单的例子,5是素数,我们拿2-5一个一个除,真的需要3跟4来除以它吗?我们假设一个数m = a * b,假设一个数小于根号m,那么另外一个必须大于根号m,即该数不是素数,那么如果m从2除到根号m依然没有被整除,这说明该数即为素数(16 = 2 * 8 = 4 * 4)。(sqrt(m)求的是根号m,注意需要引入头文件<math.h>

//境界三
#include <stdio.h>
#include <math.h>
int main()
{
	int i = 0, j = 0;
	int count1 = 0, count2 = 0;

	printf("1 —— 100之间素数有:\n");
	printf("%d ", 2);
	count2++;
	for (i = 3; i <= 100; i += 2)
	{
		for (j = 2; j <= sqrt(i); j++)
		{
			count1++;
			if (i % j == 0)
			{
				break;
			}
		}
		if (j > sqrt(i))
		{
			printf("%d ", i);
			count2++;
		}
	}

	printf("\n**************\n");
	printf("循环次数:%d\n", count1);
	printf("素数个数:%d\n", count2);
	printf("**************\n");

	return 0;
}

在这里插入图片描述
我们将上述算法和境界二结合成功让我们的循环次数下降了一个量级,但是当我们的数字范围逐渐增大,那么此处的循环次数将会以非常快的速度增长,我们不妨一试(1-1000)。
在这里插入图片描述

🌠1.4 筛选法——境界四

既然我们在境界二中提到大于2的偶数不是素数要排除,我们试想偶数都是2的倍数,后面遇到偶数都能被2整除,故其不是素数,那么3呢?4呢?,3的倍数是不是后面都不用再除,同理4,5的倍数也不用除,于是我们便诞生了筛选法
<1> 先将1挖掉(因为1不是素数)。
<2> 用2去除它后面的各个数,把能被2整除的数挖掉,即把2的倍数挖掉。
<3> 用3去除它后面的各数,把3的倍数挖掉。
<4> 分别用5…各数作为除数去除这些数以后的各数。

上述操作需要一个很大的容器去装载所有数的集合,只要满足上述条件,即2的倍数大于1的全部置0,3的倍数大于1的全部置0,4的倍数大于1的全部置0……一直到这个数据集合的末尾,这样一来不为0的数就是素数了,然后按下标在里面进行查找就好了。

#include <stdio.h>
#include <stdbool.h>
#define MAX 200
int main()
{
	bool times[MAX] = { 0 };
	int i = 0, j = 0;
	int count1 = 0, count2 = 0;
	printf("1 —— 100之间素数有:\n");
	for (i = 0; i < 100; i++)
	{
		times[i] = TRUE;
	}

	for (i = 2; i <= 100; i++)
	{
		if (times[i])
		{
			printf("%d ", i);
			count2++;
			for (j = i + i; j <= 100; j += i)
			{
				count1++;
				times[j] = FALSE;
			}
		}
	}
	printf("\n**************\n");
    printf("循环次数:%d\n", count1);
	printf("素数个数:%d\n", count2);
	printf("**************\n");
	return 0;
}

这里我们使用的容器就是bool定义的times数组bool定义的数组默认TRUE为1,FALSE为0,注意使用时需包含头文件<stdbool.h>),大小为200,将里面前100个数(因为要求1-100之间素数)全部置为1(即初始默认标记全为素数),然后拿2-100(1不是素数自动舍弃)的数一个一个的舍弃倍数,比如最里面那个for循环我们首先定义j = 2*i,每循环一次,j都加一个i,这步即表示将i对应的倍数全部标记为0(后续不判断)。

在这里插入图片描述
循环次数果然又少了很多,我们再测试一下1-1000的数。
在这里插入图片描述

循环次数相较境界三又少了上千次,果然筛选法yyds。

🌠1.5 筛选法plus——境界五

那么跟试除法一样,境界四的算法有没有优化的地方呢?当然有!,我们试想当
我们排查标记完2的倍数,这里举个6,12,这里排查2的时候已经用times数组标记了这两个数,那么当3进行判断时我们的内层for循环是不是又要重新遍历一次6,12…等等一系列数字,找到病根!我们要让后续不重复判断,那么就又减少了判断次数,和境界三同样的思路,我们让初始j的值置为i*i,是不是就能避免一部分数重复判断。

//境界五
#include <stdio.h>
#include <stdbool.h>
#define MAX 200
int main()
{
	bool times[MAX] = { 0 };
	int i = 0, j = 0;
	int count1 = 0, count2 = 0;
	printf("1 —— 100之间素数有:\n");
	for (i = 0; i < 100; i++)
	{
		times[i] = TRUE;
	}

	for (i = 2; i <= 100; i++)
	{
		if (times[i])
		{
			printf("%d ", i);
			count2++;
			for (j = i * i; j <= 100; j += i)
			{
				count1++;
				times[j] = FALSE;
			}
		}
	}
	printf("\n**************\n");
	printf("循环次数:%d\n", count1);
	printf("素数个数:%d\n", count2);
	printf("**************\n");
	return 0;
}

在这里插入图片描述
我门再测试一下1-1000的数。
在这里插入图片描述
果然又少了几百次,那咱就喜新厌旧:筛选法plus 永远滴神!

⚡2.结语

到这,我们的 《素数求解的五大境界》 已经接近尾声了,后续我还会持续更新C语言相关内容,学习永无止境,就会永远只会留给有准备的人。希望我的博客对大家有所帮助,如果喜欢的可以 点赞+收藏哦,也随时欢迎大家在评论区及时指出我的错误。
在这里插入图片描述

  • 9
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 10
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

guaabd

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值