??xml version="1.0" encoding="utf-8" standalone="yes"?>
]]>
一个数Q如果只?和它本n两个因数Q这L数叫做质敎ͼ又称素数?/p>
在上??a target=_blank>素数法大全Q及CE序实现优化详解 (一) 试除?/a>》中我们已经探讨了求解素数的一cȝ法,q且试除法从最初的低效版本优化的高效的V2。那么,q有没有其它更佳法呢?q就是下面三藏要和大家探讨的内容
合数qo{选法
法描述Q我们知道,素数N不能?~(N-1)间的M数整除;反过来看Q只要能?~(N-1)间的M数整除的NQ都不是素数。所以我们可以采用一个简单的排除法:是对N以内的所有数Q只要逐个去除gؓ2~(N-1)的倍数的数Q剩下的是素数?/p>
C语言实现
// 合数qo{选法 Ver1
// 参数Qn 求解n以内(包括n)的素?br>// q回|n以内素数个数
int CompositeNumFilterV1(int n)
{
int i, j;
// 素数数量l计
int count = 0;
// 分配素数标记I间Q结合后文思考ؓ?1
char* flag = (char*)malloc( n+1 );
// 初始化素数标?
for (i=2; i<=n; i++)
{
// Z?(p+i)要写成flag[i]呢?可读性更佛_
flag[i] = 1;
}
// 写程序要注意排版和留I,方便阅读Q也可减出错几?br> // ?~(N-1)为因子过滤合?
for (i=2; i < n; i++)
{
for (j=2; i*j <= n; j++)
{
// i*j是由i,j两整数相乘而得Q显然不是素?br> flag[i*j] = 0;
}
}
// l计素数个数
for (i=2; i<=n; i++)
{
// 其实if(flag)其同样作用了,但这么写是有留言?
// 请参阅《C语言E序设计常见错误剖析及解决之道》一?
if (1 == flag[i]) count++;
}
// 因输Ӟ且和法核心相关不大Q故?br>
// 释放内存Q别忘了传说中的内存泄漏
free(flag);
return count;
}
在上文给出的main函数中以不同参数调用CompositeNumFilterV1函数Q得到执行结果如下:
[100000]以内素数个数Q?592, 计算用时Q?5毫秒
[1000000]以内素数个数Q?8498, 计算用时Q?25毫秒
[5000000]以内素数个数Q?48513, 计算用时Q?578毫秒
[10000000]以内素数个数Q?64579, 计算用时Q?281毫秒
注:因程序是非独占性运行的Q所以时间不是完全精的Q但基本能反映实?/p>
昄Q比上文中的试除法要快,而且谁都可以看到上例是一个未l优化的_陋版本Q好多地Ҏ三藏故意采用比较低效做法Qؓ了与后文的优化版比较Q凸显优化之重要Q也Z初学者记住别采用cM低效做法Q下面我们开始优化之?/p>
优化分析
上面CompositeNumFilterV1函数存在的问题有Q?/p>
据上q分析,我们可将E序优化如下Q?/p>
// 合数qo{选法 Ver2
// 参数Qn 求解n以内(包括n)的素?br>// q回|n以内素数个数
int CompositeNumFilterV2(int n)
{
int i, j;
// 素数数量l计
int count = 0;
// 分配素数标记I间Q明?1原因了吧Q因为浪费了一个flag[0]
char* flag = (char*)malloc( n+1 );
// 初始化素数标讎ͼ要高效点?br> flag[2] = 1;
// 注意是i<n不是上例中的i<=n了,理由自?
for (i=3; i<n; i++)
{
flag[i++] = 1;
// 偶数自然不是素数Q直接置0好了
flag[i] = 0;
}
// n为奇?
if (n%2 != 0)
{
flag[n] = 1;
}
// ?开始filterQ因?的倍数早在初始化时代就q掉?br> // 到n/2止的理由q要说吗
for (i=3; i <= n/2; i++)
{
// i是合敎ͼh着吧,因ؓ您的工作早有您的质因子代劳了
if (0 == flag[i]) continue;
// 从i?倍开始过滤,变乘法ؓ加法
for (j=i+i; j <= n; j+=i)
{
flag[j] = 0;
}
}
// l计素数个数
for (i=2; i<=n; i++)
{
if (flag[i]) count++;
}
// 因输Ӟ且和法核心相关不大Q故?br>
// 释放内存Q别忘了传说中的内存泄漏
free(flag);
return count;
}
再来调用CompositeNumFilterV2得到执行l果Q?/p>
[100000]以内素数个数Q?592, 计算用时Qn太小Q时间精度不?br>[1000000]以内素数个数Q?8498, 计算用时Q?1毫秒
[5000000]以内素数个数Q?48513, 计算用时Q?53毫秒
[10000000]以内素数个数Q?64579, 计算用时Q?062毫秒
[100000000]以内素数个数Q?761455, 计算用时Q?2973毫秒
哇哇Q比昨天的试除发快了好多倍,可见法的威力,值得好好学习Q别说学法没用咯?/p>
上例着那个计算一亿以内的素数只要U?3U,应该不错了Q今天是否可以休息了呢?NoQ我们要q求极限Q?/p>
int CompositeNumFilterV3(int n)
{
int i, j;
// 素数数量l计
int count = 0;
// 分配素数标记I间Q明?1原因了吧Q因为浪费了一个flag[0]
char* flag = (char*)malloc( n+1 );
// q嘛用的Q请仔细研究下文
int mpLen = 2*3*5*7*11*13;
char magicPattern[mpLen];
// 奇怪的代码QwhyQ思考无法代劻I惻I
for (i=0; i<mpLen; i++)
{
magicPattern[i++] = 1;
magicPattern[i++] = 0;
magicPattern[i++] = 0;
magicPattern[i++] = 0;
magicPattern[i++] = 1;
magicPattern[i] = 0;
}
for (i=4; i<=mpLen; i+=5)
magicPattern[i] = 0;
for (i=6; i<=mpLen; i+=7)
magicPattern[i] = 0;
for (i=10; i<=mpLen; i+=11)
magicPattern[i] = 0;
for (i=12; i<=mpLen; i+=13)
magicPattern[i] = 0;
// 新的初始化方??,3,5,7,11,13的倍数全干?br> // 而且采用memcpy以mpLen长的magicPattern来批量处?
int remainder = n%mpLen;
char* p = flag+1;
char* pstop = p+n-remainder;
while (p < pstop)
{
memcpy(p, magicPattern, mpLen);
p += mpLen;
}
if (remainder > 0)
{
memcpy(p, magicPattern, remainder);
}
flag[2] = 1;
flag[3] = 1;
flag[5] = 1;
flag[7] = 1;
flag[11] = 1;
flag[13] = 1;
// ?7开始filterQ因?,3,5,7,11,13的倍数早被kill?
// 到n/13止的Q哈哈,了好多?br> int stop = n/13;
for (i=17; i <= stop; i++)
{
// i是合敎ͼh着吧,因ؓ您的工作早有您的质因子代劳了
if (0 == flag[i]) continue;
// 从i?7倍开始过?br> int step = i*2;
for (j=i*17; j <= n; j+=step)
{
flag[j] = 0;
}
}
// l计素数个数
for (i=2; i<=n; i++)
{
if (flag[i]) count++;
}
// 因输Ӟ且和法核心相关不大Q故?br>
// 释放内存Q别忘了传说中的内存泄漏
free(flag);
return count;
}
再看CompositeNumFilterV3执行l果Q?/p>
[1000000]以内素数个数Q?8498, 计算用时Q?5毫秒
[5000000]以内素数个数Q?48513, 计算用时Q?03毫秒
[10000000]以内素数个数Q?64579, 计算用时Q?15毫秒
[100000000]以内素数个数Q?761455, 计算用时Q?421毫秒
再次优化后速度提升了又一倍左叻I三藏不禁有点满了,睡觉也!
质数的定?/strong>
一个数Q如果只?和它本n两个因数Q这L数叫做质敎ͼ又称素数?nbsp;
试除判断?/strong>
法描述Q从上述定义可知Q素C能被1和它本n之外的数整除Q所以,判断一个数x是否素数只要看它是否能被2~sqrt(x)间的数整除即可;而求N内所有素数则是@环重复上q过E?/p>
C语言实现Q?/p>
#include <time.h>
#include <malloc.h>
#define N 100000
// 单试除判断法 Ver1
int SimpleDivisionV1(int n)
{
int i,j;
// 素数数量l计
int count = 0;
// 分配存放l果的空?
int* primes = (int*)malloc( sizeof(int)*n );
// 2是素数谁都知道,不算?
primes[count++] = 2;
// 循环计算3~n间的?
for (i=3; i<=n; i++)
{
// Z么是sqrt(i)Q思考一?
for (j=2; j<=sqrt(i); j++)
{
// i被j整除Q显然不是素C
if (i%j == 0) break;
}
// i不能?~sqrt(i)间的数整?素数?
if (j > sqrt(i))
{
primes[count++] = i;
}
}
// 因输Ӟ且和法核心相关不大Q故?br>
// 释放内存Q别忘了传说中的内存泄漏
free(primes);
return count;
}
void main()
{
int count;
clock_t start, end;
// time函数不够_Q用clock凑合一下吧
start = clock();
count = SimpleDivisionV1(N);
end = clock();
printf("[%d]以内素数个数Q?d, 计算用时Q?d毫秒\n", N, count, end-start);
getch();
}
计算l果Q?br>[100000]以内素数个数Q?592, 计算用时Q?68毫秒
[1000000]以内素数个数Q?8498, 计算用时Q?0859毫秒
[5000000]以内素数个数Q?48513, 计算用时Q?03560毫秒
噢噢Q算十万还行,百万?0U多了,而且旉增长很快Q这不行Q得优化一下!
优化分析Q?/p>
仔细研究一下SimpleDivisionV1我们可以发现以下几个问题Q?/p>
Ҏ上面两点Q我们可SimpleDivisionV1升为SimpleDivisionV2Q如?/p>
// 单试除判断法 Ver2
int SimpleDivisionV2(int n)
{
int i, j, k, stop;
// 素数数量l计
int count = 0;
// 分配存放l果的空?
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++;
// stopq什么用Q思考一?br> for (j=0; j<stop; j++)
{
if (i%primes[j] == 0) break;
}
// i不能?~sqrt(i)间的素数整除Q自然也不能被其他数整除,素数?
if (j == stop)
{
primes[count++] = i;
}
}
// 因输Ӟ且和法核心相关不大Q故?br>
// 释放内存Q别忘了传说中的内存泄漏
free(primes);
return count;
}
然后main中调用的函数替换为SimpleDivisionV2Q在看一下执行结果:
[100000]以内素数个数Q?592, 计算用时Q?6毫秒
[1000000]以内素数个数Q?8498, 计算用时Q?46毫秒
[5000000]以内素数个数Q?48513, 计算用时Q?515毫秒
[10000000]以内素数个数Q?64579, 计算用时Q?000毫秒
很开心的看到Q经q优化,速度提高了几十倍,其是时间增长曲U的坡度变小了,ND大,V2函数比V1的效率就高
对于试除判断q种质数法来说Q三藏认为SimpleDivisionV2基本已经接近极限Q不大可能有量上的H破了,有兴的朋友可以自己q一步优化。初学者除了参看上qC子外Q可以尝试做各种修改及细节优化,也可以将除法变乘法,多加l习是学习编E的好方法?/p>
虽然Q上例中V2已经比V1快了很多了,但随着N的增大,耗时q是不少Q那么我们还有更好的Ҏ吗?
里面包括Q?/p>
U性表——静态链?009.7.30
U性表——链表的内部cd?008.8
U性表——链表的友元cd?008.8
U性表——双向链?009.8.11
U性表——顺序表2009.8.3
U性表——友元模板类2009.8
U瑟夫问题——四U解?009.8.11Q数l,链表Q@环链表等Q?br>队列——队列的序表示循环队列2009.8.11
队列——链队列2009.8.11
栈——汉诺塔2009.8.6
栈——进制{?009.8.6
栈——括号匹?009.8.6
栈——模杉K?009.8.3
栈——模杉K序栈2009.7
栈——顺序栈2009.8.6
栈——算术表辑ּ求?009.7.13
栈——行~辑2009.8.6
二叉树——二叉树的常见操?009.9.3
2010.3.9 更新
树的应用—仿DOS文g夹管理程?/p>
200位大C?rar
200位大数加?rar
q连看单机程序MFC