打印素数的各种算法

以前在网上看过一段这么描述素数的话说数论的研究对象归根到底是对素数的研究,因此如何高效地判断一个数是否是素数或者快速打印出一定范围内的素数的重要性不言而喻。下面我们来看一下几种关于素数判别的算法。

1、素数判定朴素算法

素数是这样一类特殊的数:它的因子只有1和它本身。由此我们可以得一种最为简单的判断一个数是否为素数的算法:对于某个数n,使2至n的开根作为除数,如果这些除数都不能整除n,则说明n是素数。这样的算法思路简单,但是时间复杂度却达到O(n*lg(n)),因此并不是高效的算法。

#include <stdio.h>
#include <math.h>
 
bool isPrime(int n)
{
    if(n < 2)
        return false;
    for(int i = 2; i <= sqrt(n); ++i)
    {
        if(!(n % i))
        {
             return false;
        }
    }
    return true;
}
 
int main()
{
    for(int i = 0; i <= 50; ++i)
    {
        if(isPrime(i))
        {
             printf("%d\n", i);
        }
    }
    return 0;
}
2、利用筛选法求出指定范围内的素数

按照1中的方法来求素数,虽然可以达到目的,但是时间复杂度较高,是我们所不能容忍的,因此需要对上述方法进行改进。在通常所使用的算法中,一般时间复杂度较低的算法大多是利用筛选法,即将不符合条件的元素逐一排除,最后在集合中的元素都是满足条件的。求解素数同样可以利用这种方法,例如需要打印20以内的素数,则有

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

第一步:取出第一个数2,去除集合中其他能被2整除的数。

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

第二步:取出第二个数3,去除集合中其他能被3整除的数。

2 3 5 7 11 13 17 19 23 25 29

第三步:取第三个数5,去除集合中其他能被5带队的数。

2 3 5 7 11 13 17 19 23 29

第四步:取第四个数7,因为7的平方大于30,因此中止操作,此时集合中剩余的数都是素数。

这种方法的时间复杂度较方法1有所提高,达到O(nlg(n))。

3、利用文件节约运行时间

方法2虽然对方法1有所改进,但是在工程应用中的可操作性并不强,主要是因为它们在运行时需要耗费很多时间来判断一个数是否为素数。因为我们可以从这个方面来着手改进,使得在工程应用上更加高效。

在实际应用中,我们一般是将素数事先计算好,保存在文件中(因为指定范围的素数是固定的,因此可以这样处理),在程序启动时,加载这个文件,不管是打印还是查找操作都可以在O(lg(n))(二分查找)甚至O(1)(Hash查找)时间内完成。

其实很多应用都是将数据事先计算出来保存到文件中,在程序运行时直接读取文件,从而避免计算所耗费的大量时间。例如我们启动软件时读取的配置文件,以及游戏程序里的特效数据文件。

4、编译时完成数据计算节约运行时间

随着计算机硬件技术的发展,内存已经不再是奢侈品,而且从内存中读取数据的速度要远远快于从硬盘读取数据。因此在实际应用中可以将运行时花费较多时间计算的数据,在编译时完成计算保存在内存中,那么在运行时可以直接在内存中读取这部分数据,从而大大节约软件运行时间。

在C++中,利用编译器对模板的特化处理可以实现在编译期计算指定范围内的素数,并在运行时直接将计算出的结果打印出来。

在JAVA Web开发中,涉及到大量数据处理的程序可以做成启动型servlet,将Web程序部署到Tomcat服务器中,在Tomcat启动时,servlet中的程序优先执行,将计算得到的数据保存至服务器内存中,方便用户使用。

5、正则表达式判定素数

在网上看到其他人提到可以利用正则表达式可以实现判定指定的数是否为素数。具体的正则表达式为

^1?$|^(11+?)\1+$
使用这种方法需要将给定的数转化为全“1”的字符串,例如2要写成“11”,3要写成“111”,10要写成”1111111111“。测试程序如下:

<script type="text/javascript">
var st="1";
document.write("1~100之间的所有素数如下:", "<br/>");
for(var i = 2; i < 100; i++)
{
	if(!/^1?$|^(11+?)\1+$/.test(st = st + "1"))
	{
		document.write(i, "<br/>");
	}
}
document.close();
</script>

原理:加号表示匹配一次或多次(加上一个问号表示非贪婪模式),\1表示引用括号里的内容,头尾的^和$则避免了部分匹配的情况。这样,^(11+?)相当于枚举除数大小,而\1+$则用于检验整个字符串是否能按此大小恰好分完。如果除得尽,则匹配成功,字符串长度为合数。另外,前面的^1?$只是为了处理n=0或n=1时的特殊情况,而符号|则表示“或者”的意思。


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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值