计算素数的算法(一)
2014-06-11 20:13
204 查看
经常有初学者询问求解N内所有素数(质数)的问题,对此,网上的解答也很多,但很多要么不够专业,要么只有程序没有算法解析,所以三藏大厦对此问题做个小结,探讨一下求解素数的常见算法,同时给出相应的C语言程序及其解析。为了方便初学者理解,本文将从易到难阐述不同算法,高手可以直接看后面的高效算法
质数的定义
一个数,如果只有1和它本身两个因数,这样的数叫做质数,又称素数。
试除判断法
算法描述:从上述定义可知,素数不能被1和它本身之外的数整除,所以,判断一个数x是否素数只要看它是否能被2~sqrt(x)间的数整除即可;而求N内所有素数则是循环重复上述过程。
C语言实现:
复制内容到剪贴板
代码:
#include <time.h>
#include <malloc.h>
#define N 100000
// 简单试除判断法 Ver1
int s i m p l eDivisionV1(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 = s i m p l eDivisionV1(N);
end = clock();
printf("[%d]以内素数个数:%d, 计算用时:%d毫秒\n",
N, count, end-start);
getch();
}
计算结果:
[100000]以内素数个数:9592, 计算用时:468毫秒
[1000000]以内素数个数:78498, 计算用时:10859毫秒
[5000000]以内素数个数:348513, 计算用时:103560毫秒
噢噢,算算十万还行,百万就10秒多了,而且时间增长很快,这不行,得优化一下!
优化分析:
仔细研究一下s i m p l eDivisionV1我们可以发现以下几个问题:
1. 在循环条件中重复调用sqrt(i)显然是比较浪费时间的
2. 判断素数,真的需要拿2~sqrt(i)间的所有整数去除吗?我们知道,合数都可以分解成若干质数,所以只要2~sqrt(i)间的质数不能整除i即可
根据上面两点,我们可将s i m p l eDivisionV1升级为s
i m p l eDivisionV2,如下
复制内容到剪贴板
代码:
// 简单试除判断法 Ver2
int s i m p l eDivisionV2(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中调用的函数替换为s
i m p l eDivisionV2,在看一下执行结果:
[100000]以内素数个数:9592, 计算用时:46毫秒
[1000000]以内素数个数:78498, 计算用时:546毫秒
[5000000]以内素数个数:348513, 计算用时:3515毫秒
[10000000]以内素数个数:664579, 计算用时:8000毫秒
很开心的看到,经过优化,速度提高了几十倍,尤其是时间增长曲线的坡度变小了,N值越大,V2函数比V1的效率就越高
对于试除判断这种质数算法来说,三藏认为s i m p l eDivisionV2基本已经接近极限,不大可能有量级上的突破了,有兴趣的朋友可以自己进一步优化。初学者除了参看上述例子外,可以尝试做各种修改及细节优化,也可以将除法变乘法,多加练习是学习编程的好方法。
虽然,上例中V2已经比V1快了很多了,但随着N的增大,耗时还是不少,那么我们还有更好的方法吗?
质数的定义
一个数,如果只有1和它本身两个因数,这样的数叫做质数,又称素数。
试除判断法
算法描述:从上述定义可知,素数不能被1和它本身之外的数整除,所以,判断一个数x是否素数只要看它是否能被2~sqrt(x)间的数整除即可;而求N内所有素数则是循环重复上述过程。
C语言实现:
复制内容到剪贴板
代码:
#include <time.h>
#include <malloc.h>
#define N 100000
// 简单试除判断法 Ver1
int s i m p l eDivisionV1(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 = s i m p l eDivisionV1(N);
end = clock();
printf("[%d]以内素数个数:%d, 计算用时:%d毫秒\n",
N, count, end-start);
getch();
}
计算结果:
[100000]以内素数个数:9592, 计算用时:468毫秒
[1000000]以内素数个数:78498, 计算用时:10859毫秒
[5000000]以内素数个数:348513, 计算用时:103560毫秒
噢噢,算算十万还行,百万就10秒多了,而且时间增长很快,这不行,得优化一下!
优化分析:
仔细研究一下s i m p l eDivisionV1我们可以发现以下几个问题:
1. 在循环条件中重复调用sqrt(i)显然是比较浪费时间的
2. 判断素数,真的需要拿2~sqrt(i)间的所有整数去除吗?我们知道,合数都可以分解成若干质数,所以只要2~sqrt(i)间的质数不能整除i即可
根据上面两点,我们可将s i m p l eDivisionV1升级为s
i m p l eDivisionV2,如下
复制内容到剪贴板
代码:
// 简单试除判断法 Ver2
int s i m p l eDivisionV2(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中调用的函数替换为s
i m p l eDivisionV2,在看一下执行结果:
[100000]以内素数个数:9592, 计算用时:46毫秒
[1000000]以内素数个数:78498, 计算用时:546毫秒
[5000000]以内素数个数:348513, 计算用时:3515毫秒
[10000000]以内素数个数:664579, 计算用时:8000毫秒
很开心的看到,经过优化,速度提高了几十倍,尤其是时间增长曲线的坡度变小了,N值越大,V2函数比V1的效率就越高
对于试除判断这种质数算法来说,三藏认为s i m p l eDivisionV2基本已经接近极限,不大可能有量级上的突破了,有兴趣的朋友可以自己进一步优化。初学者除了参看上述例子外,可以尝试做各种修改及细节优化,也可以将除法变乘法,多加练习是学习编程的好方法。
虽然,上例中V2已经比V1快了很多了,但随着N的增大,耗时还是不少,那么我们还有更好的方法吗?
相关文章推荐
- 计算素数算法的一些优化(编程珠玑阅读感想)
- 算法1.1 最大公约数(欧几里得)&判定素数&计算平方根(牛顿迭代法)
- 一个简单的计算素数的算法
- LeetCode: Count Primes(计算n以内素数个数:高效算法)
- 计算一定范围内素数个数的算法
- 一道经典面试题:计算n以内的素数(质数)算法
- C/C++编程小练习 计算10亿之内的素数和(合数筛选算法实现)
- <编程>比较两种素数表生成算法+计算程序运行时间+通过CMD重定向测试程序
- 高效率的算法 计算100亿内的素数的个数 3.01G的cpu运算只用8.64秒!!!!
- 高效的计算素数的算法--埃氏筛法
- SAM的散列存储加密解密算法以及SYSKEY的计算
- “算法与计算数学”之四书五经
- 一个计算完全数的算法
- “算法与计算数学”之四书五经
- 计算任何一天是星期几的几种算法 转自asp.net
- “算法与计算数学”之四书五经
- 产生素数的算法
- 计算任意三边三角形的面积的算法
- 素数生成器的算法【PHP EDITION】
- 求找素数的最佳算法