(ZZ)素数算法大全,及C程序实现优化详解 (一) 试除法

本文探讨了求解N内所有素数的常见算法,并提供了C语言程序及其解析。从试除判断法出发,逐步优化算法,提高求解素数的效率。
经常有初学者询问求解N内所有素数(质数)的问题,对此,网上的解答也很多,但很多要么不够专业,要么只有程序没有算法解析,所以三藏大厦对此问题做个小结,探讨一下求解素数的常见算法,同时给出相应的C语言程序及其解析。为了方便初学者理解,本文将从易到难阐述不同算法,高手可以直接看后面的高效算法

质数的定义

一个数,如果只有1和它本身两个因数,这样的数叫做质数,又称素数。 

试除判断法

算法描述:从上述定义可知,素数不能被1和它本身之外的数整除,所以,判断一个数x是否素数只要看它是否能被2~sqrt(x)间的数整除即可;而求N内所有素数则是循环重复上述过程。

C语言实现

#include <time.h>
#include <malloc.h> 
#define N 100000
// 简单试除判断法 Ver1 
int SimpleDivisionV1(int n)
{
 int i,j;
 // 素数数量统计 
 int count = 0;
 // 分配存放结果的空间 
 int* primes = (int*)malloc( sizeof(int)*n ); 
 
 // 2是素数谁都知道,不算了 
 primes[count++] = 2;
 // 循环计算3~n间的数 
 for (i=3; i<=n; i++)
 {
  // 为什么是sqrt(i),思考一下 
  for (j=2; j<=sqrt(i); j++)
  {
   // i被j整除,显然不是素数了 
   if (i%j == 0) break;
  }
  // i不能被2~sqrt(i)间的数整除,素数也 
  if (j > sqrt(i))
  {
   primes[count++] = i;
  }
 }
 
 // 因输出费时,且和算法核心相关不大,故略
  
 // 释放内存,别忘了传说中的内存泄漏 
 free(primes);
 
 return count;
} 

void main()
{
 int count;
 clock_t start, end;
 // time函数不够精确,用clock凑合一下吧 
 start = clock(); 
 count = SimpleDivisionV1(N);
 
 end = clock();
 printf("[%d]以内素数个数:%d, 计算用时:%d毫秒\n", N, count, end-start);
 getch(); 
}

计算结果:
[100000]以内素数个数:9592, 计算用时:468毫秒
[1000000]以内素数个数:78498, 计算用时:10859毫秒
[5000000]以内素数个数:348513, 计算用时:103560毫秒

噢噢,算算十万还行,百万就10秒多了,而且时间增长很快,这不行,得优化一下!

优化分析

仔细研究一下SimpleDivisionV1我们可以发现以下几个问题:

  1. 在循环条件中重复调用sqrt(i)显然是比较浪费时间的
  2. 判断素数,真的需要拿2~sqrt(i)间的所有整数去除吗?我们知道,合数都可以分解成若干质数,所以只要2~sqrt(i)间的质数不能整除i即可

根据上面两点,我们可将SimpleDivisionV1升级为SimpleDivisionV2,如下

 // 简单试除判断法 Ver2 
int SimpleDivisionV2(int n)
{
 int i, j, k, stop;
 // 素数数量统计 
 int count = 0;
 // 分配存放结果的空间 
 int* primes = (int*)malloc( sizeof(int)*n ); 
 
 // 2是素数谁都知道,不算了 
 primes[count++] = 2;
 stop = count;
 // 循环计算3~n间的数 
 for (i=3; i<=n; i++)
 {
  k = sqrt(i);
  // 在循环条件中重复调用sqrt是低效做法,故引入k 
  while (primes[stop] <= k && stop < count)
   stop++;
  // stop干什么用,思考一下
  for (j=0; j<stop; j++)
  {
   if (i%primes[j] == 0) break;
  }
  // i不能被2~sqrt(i)间的素数整除,自然也不能被其他数整除,素数也 
  if (j == stop)
  {
   primes[count++] = i;
  }
 }
 
 // 因输出费时,且和算法核心相关不大,故略
  
 // 释放内存,别忘了传说中的内存泄漏 
 free(primes);
 
 return count;
}

然后将main中调用的函数替换为SimpleDivisionV2,在看一下执行结果:

[100000]以内素数个数:9592, 计算用时:46毫秒
[1000000]以内素数个数:78498, 计算用时:546毫秒
[5000000]以内素数个数:348513, 计算用时:3515毫秒
[10000000]以内素数个数:664579, 计算用时:8000毫秒

很开心的看到,经过优化,速度提高了几十倍,尤其是时间增长曲线的坡度变小了,N值越大,V2函数比V1的效率就越高

对于试除判断这种质数算法来说,三藏认为SimpleDivisionV2基本已经接近极限,不大可能有量级上的突破了,有兴趣的朋友可以自己进一步优化。初学者除了参看上述例子外,可以尝试做各种修改及细节优化,也可以将除法变乘法,多加练习是学习编程的好方法。

虽然,上例中V2已经比V1快了很多了,但随着N的增大,耗时还是不少,那么我们还有更好的方法吗?

转载于:https://my.oschina.net/u/1178546/blog/146774

评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符  | 博主筛选后可见
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值