求素数,算法简化

7 篇文章 0 订阅

引子
谈起求素数的问题,可能许多人嗤之以鼻,认为是编程学习最简单最基础的一部分,可是,真的是这样吗?
试问:当一个数足够大,或者对该算法的时间有严格的限制时,还是如此简单吗?
正文
乍眼一看,第一反应应该是这样的:

#include<stdio.h>
#include<Windows.h>
#include<time.h>

int IsPrime(int num)
{
	int i;
	if (num <= 1){
		return 0;
	}
	if (num == 2){
		return 1;
	}
	for (i = 2; i < num; i++){
		if (num%i == 0){
			return 0;
		}
	}
	return 1;
}
int main()
{
	int i,top = 100000;
	int start = clock();
	for (i = 2; i <= top; i++){
		if (IsPrime(i)){
			printf("%d ", i);
		}
	}
	int end = clock();
	printf("\n运行时间:%d\n", end-start);
	system("pause");
	return 0;
}

一个数除以比本身一半还要大的数,怎么可能除净!在判断的时候,每次都会判断n-2次,大大增加了程序运行的时间,所以,这种做法肯定是不可取的。此时,程序用时2520ms

一个数除以自身一半以上的数时,是不会除尽的,所以,可以在原程序上做简单修改,将IsPrime函数中的素数判断条件由num修改为num/2后,程序用时1709ms,完成算法的初步简化

可以继续在原程序上修改,引入#include<math.h>头文件后,将素数判断条件由上一步的n/2修改为sqrt(num),此时,程序用时842ms,完成算法的进一步简化。(由于因数是成对出现的,一个数的因数小于等于该数的开平方时,这个数的另一个因数势必会大于等于该数的开平方,例如:100,它的因数有1和100,2和50,4和25,5和20,10和10,可知,100的一个因数小于等于10时,与该因数成对的另一个因数必定大于等于10..........)

由此,可以通过程序运行的时间反馈看出,经过两次简化后,运行时间缩短了几近1/3

当然,由于素数除了2之外其他全是奇数,所以,也可以每次加2,减少判断数的个数;同时,判断时首先排除所有的偶数,然后在剩下的数中枚举进行判断

所以,经过仔细的深究后是这样的:

#include<stdio.h>
#include<Windows.h>
#include<time.h>
#include<math.h>

int IsPrime(int num)
{
	int i;
	if (num % 2 == 0){
		return 0;
	}
	for (i = 3; i < sqrt(num); i+=2){
		if (num%i == 0){
			return 0;
		}
	}
	return 1;
}
int main()
{
	int i,top = 100000;
	int start = clock();
	printf("2 ");
	for (i = 3; i <= top; i+=2){
		if (IsPrime(i)){
			printf("%d ", i);
		}
	}
	int end = clock();
	printf("\n运行时间:%d\n", end-start);
	system("pause");
	return 0;
}

这样的程序,对于普通的编程来说已经够用了,但是,对于有严格要求的算法似乎还是不太够用。

说完了试除法,下面来简单谈谈筛选法

随着对于算法及编程学习的不断深入,我们会接触到 埃拉托斯特尼筛法 -----由希腊著名的数学家提出的

其算式简述为:

要得到自然数n以内的全部素数,必须把不大于\sqrt{n}的所有素数的倍数剔除,剩下的就是素数

给出要筛数值的范围n,找出以内的素数。先用2去筛,即把2留下,把2的倍数剔除掉;再用下一个素数,也就是3筛,把3留下,把3的倍数剔除掉;接下去用下一个素数5筛,把5留下,把5的倍数剔除掉;不断重复下去......

例如:列出2以后的所有数为

2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 ......

先用2去筛,筛去除2以外的2的所有倍数

2 3 5 7 9 11 13 15 17 19 21 23 25 ......

再用3去筛,筛去除3以外的3的所有倍数

2 3 5 7 11 13 17 19 23 25 ......

再用5去筛,筛去除5以外的5的所有倍数

2 3 5 7 11 13 17 19 23 ...... 

...... ...... 

直到筛选的数为空为止

#include<stdio.h>
#include<Windows.h>
#include<time.h>

#define N 100000

int flag[100000];

void IsPrime()
{
	int i, j;
	for (i = 2; i <= N; i++){   //InitArray
		flag[i] = 1;
	}
	for (i = 2; i <= N; i++){
		if (flag[i]){
			printf("%d ", i);
		}
		for (j = 2 * i; j <= N; j += i){
			flag[j] = 0;
		}
	}
}
int main()
{
	int start = clock();
	IsPrime();
	int end = clock();
	printf("\n程序运行时间:%d",end-start);
	system("pause");
	return 0;
}

此算法用一个数组存储所有可能为素数的数的状态(数组下标即表示该数),先初始化,再逐个最小素数的筛选其倍数,将状态置为0,再输出

当然,该算法还存在许多可以优化的地方,比如对同一数的状态会进行多次访问、空间占用比试除法占用的多............

 

 


 


 

 

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值