说起空间换时间,想到c/c++语言的话我会想到#define宏定义和内联函数,他们都减少了函数切换时的压栈清栈等工作.对一个简短的函数,的确这些额外的消耗太浪费了.对于"计算素数"的问题,是一个经典的此类问题.
我们常用来找一个范围内的素数(质数)的办法有两种:
(1)筛选法
(2)判定法,即定义法
如:求 1— 100间的所有素数。
分析:
用筛选法,先把 2— 100的数存到一个数组中,然后先把 2的所有倍数删除掉(即让此数变为 0),再删 3的倍数,继续往上就是 5的倍数, 7的倍数……,最后,剩下的数 (即数组中不为 0的数 )就是素数。
用判定法,对2-100内的每个数,根据素数的定义,除本身外没有其它约数来判定是否为素数.这个有个注意的地方是:找约数的时候不用找到n,也不用找到n/2,只要到sqrt(n)就行了.[别问为什么?自己想去咯.] 具体是<?, 这里有int 和double 的比较,有写sqrt(n)+1,也看到有人写sqrt(n+1) 的,哪个是正解自己多思考呵.
验证:
以下是筛选法代码:
下面看我们的结果:
我测试了从1-1000000内的素数: 耗时和统计结束如下:
it takes your 2.000000 seconds in filter.
filter(end): count = 78498
it takes your 5.000000 seconds in judge.
judge(end): count = 78498
Press any key to continue
这说明在N很大时,筛选法体现出了它的高效.在N比较小时,则看不出来其明显优势咯.筛选法用了很多的内存放要被处理的数据.但是此算法对内存的访问是顺序的,在经过有选择的取出除数(筛选器?)来否定一些保留一些.经过相对比较少的次数完成了对全部数据的筛选工作.在算法复杂度上,尽管还是O(n^2)[与判定法没有什么区别],但事实在其执行次数和访问速度得到了很大的提高.
当然,以上的筛选算法还是可以再改进一下的.
以上的算法没有实质改进,不过其所用的内存少了一半,在进行比较等操作时循环也只有原来一半,所用的总时间和上面的筛选算法比也只是一半多一点,我觉得最重要的是,申请的内存少了,函数失败可能性变少,而且这个改进是比较有必要的,若任由它作无谓的计算,我会很心痛咯.呵呵.
注:虽然因判定法只用了几个变量,我想声明为寄存器变量,可是你知道的,"尽可能"并不是"一定",何况即使得到了,还不够快.硬件和软件算法的改善都有效果,硬件要成本,算法要技术。怎么办?掠拌.
我们常用来找一个范围内的素数(质数)的办法有两种:
(1)筛选法
(2)判定法,即定义法
如:求 1— 100间的所有素数。
分析:
用筛选法,先把 2— 100的数存到一个数组中,然后先把 2的所有倍数删除掉(即让此数变为 0),再删 3的倍数,继续往上就是 5的倍数, 7的倍数……,最后,剩下的数 (即数组中不为 0的数 )就是素数。
用判定法,对2-100内的每个数,根据素数的定义,除本身外没有其它约数来判定是否为素数.这个有个注意的地方是:找约数的时候不用找到n,也不用找到n/2,只要到sqrt(n)就行了.[别问为什么?自己想去咯.] 具体是<?, 这里有int 和double 的比较,有写sqrt(n)+1,也看到有人写sqrt(n+1) 的,哪个是正解自己多思考呵.
验证:
以下是筛选法代码:
long
filter(
int
end)
//
method: filter 1..end
{
start = time(NULL);
long count = 0 ;
long len = end + 1 ;
int i,j,k;
if (end < 2 ) return 0 ;
// 生成筛选集
int * p = new int [len];
if (p == NULL) return 0 ;
for (i = 0 ; i < len; ++ i){
p[i] = i;
}
// 筛选算法
k = 2 ; // 作为筛选的除数
i = k;
int max_test = sqrt(end) + 1 ;
while (i < max_test){
for (j = k + 1 ; j < len; ++ j)
{
if ( 0 == p[j])
continue ;
else if (p[j] % i == 0 ) {
p[j] = 0 ;
}
}
for (j = k + 1 ;j < len; ++ j)
{
if (p[j] != 0 ){
k = j;
break ;
}
}
i = k;
}
// 打印结果
for (i = 2 ;i < end; ++ i)
if (p[i] != 0 )
{
count ++ ;
// printf("%d\t",p[i]);
}
end = time(NULL);
printf( " \nit takes your %f seconds in filter.\n " ,difftime(end,start));
printf( " filter(end): count = %ld " ,count);
delete[] p;
return count;
}
以下是判定法代码:
{
start = time(NULL);
long count = 0 ;
long len = end + 1 ;
int i,j,k;
if (end < 2 ) return 0 ;
// 生成筛选集
int * p = new int [len];
if (p == NULL) return 0 ;
for (i = 0 ; i < len; ++ i){
p[i] = i;
}
// 筛选算法
k = 2 ; // 作为筛选的除数
i = k;
int max_test = sqrt(end) + 1 ;
while (i < max_test){
for (j = k + 1 ; j < len; ++ j)
{
if ( 0 == p[j])
continue ;
else if (p[j] % i == 0 ) {
p[j] = 0 ;
}
}
for (j = k + 1 ;j < len; ++ j)
{
if (p[j] != 0 ){
k = j;
break ;
}
}
i = k;
}
// 打印结果
for (i = 2 ;i < end; ++ i)
if (p[i] != 0 )
{
count ++ ;
// printf("%d\t",p[i]);
}
end = time(NULL);
printf( " \nit takes your %f seconds in filter.\n " ,difftime(end,start));
printf( " filter(end): count = %ld " ,count);
delete[] p;
return count;
}
long
judge(
int
beg,
int
end)
//
method: judge every number
{
start = time(NULL);
int count = 0 ;
if (beg > end || beg < 1 ) return 0 ;
register int i;
while (beg < end + 1 )
{
for (i = 2 ;i < sqrt(beg) + 1 ; ++ i){
if (beg % i == 0 ) break ;
}
if (i > sqrt(beg))
{ // printf("%d\t",beg);
count ++ ;
// NULL;
}
beg ++ ;
}
end = time(NULL);
printf( " \nit takes your %f seconds in judge.\n " ,difftime(end,start));
printf( " judge(end): count = %ld\n " ,count);
return count;
}
{
start = time(NULL);
int count = 0 ;
if (beg > end || beg < 1 ) return 0 ;
register int i;
while (beg < end + 1 )
{
for (i = 2 ;i < sqrt(beg) + 1 ; ++ i){
if (beg % i == 0 ) break ;
}
if (i > sqrt(beg))
{ // printf("%d\t",beg);
count ++ ;
// NULL;
}
beg ++ ;
}
end = time(NULL);
printf( " \nit takes your %f seconds in judge.\n " ,difftime(end,start));
printf( " judge(end): count = %ld\n " ,count);
return count;
}
下面看我们的结果:
我测试了从1-1000000内的素数: 耗时和统计结束如下:
it takes your 2.000000 seconds in filter.
filter(end): count = 78498
it takes your 5.000000 seconds in judge.
judge(end): count = 78498
Press any key to continue
这说明在N很大时,筛选法体现出了它的高效.在N比较小时,则看不出来其明显优势咯.筛选法用了很多的内存放要被处理的数据.但是此算法对内存的访问是顺序的,在经过有选择的取出除数(筛选器?)来否定一些保留一些.经过相对比较少的次数完成了对全部数据的筛选工作.在算法复杂度上,尽管还是O(n^2)[与判定法没有什么区别],但事实在其执行次数和访问速度得到了很大的提高.
当然,以上的筛选算法还是可以再改进一下的.
long
filter2(
int
end)
//
method: filter 1..end
{
start = time(NULL);
long count = 0 ;
long len = (end + 1 ) / 2 ;
int i,j,k;
if (end < 2 ) return 0 ;
// 生成筛选集
int * p = new int [len];
if (p == NULL) return 0 ;
for (i = 0 ; i < len; ++ i){
p[i] = i * 2 + 1 ;
}
// 筛选算法
k = 1 ; // 作为筛选的除数在数组中的下标
i = k;
int max_test = sqrt(end) + 1 ;
while (p[i] < max_test){
for (j = k + 1 ;j < len; ++ j)
{
if ( 0 == p[j])
continue ;
else if (p[j] % p[i] == 0 ) {
p[j] = 0 ;
}
}
for (j = k + 1 ;j < len; ++ j)
{
if (p[j] != 0 )
{
k = j;
break ;
}
}
i = k;
}
// 打印结果
for (i = 1 ;i < len; ++ i)
if (p[i] != 0 )
{
count ++ ;
// printf("%d\t",p[i]);
}
end = time(NULL);
printf( " \nit takes your %f seconds in filter.\n " ,difftime(end,start));
printf( " filter2(end): count = %ld " ,count);
delete[] p;
return count;
}
{
start = time(NULL);
long count = 0 ;
long len = (end + 1 ) / 2 ;
int i,j,k;
if (end < 2 ) return 0 ;
// 生成筛选集
int * p = new int [len];
if (p == NULL) return 0 ;
for (i = 0 ; i < len; ++ i){
p[i] = i * 2 + 1 ;
}
// 筛选算法
k = 1 ; // 作为筛选的除数在数组中的下标
i = k;
int max_test = sqrt(end) + 1 ;
while (p[i] < max_test){
for (j = k + 1 ;j < len; ++ j)
{
if ( 0 == p[j])
continue ;
else if (p[j] % p[i] == 0 ) {
p[j] = 0 ;
}
}
for (j = k + 1 ;j < len; ++ j)
{
if (p[j] != 0 )
{
k = j;
break ;
}
}
i = k;
}
// 打印结果
for (i = 1 ;i < len; ++ i)
if (p[i] != 0 )
{
count ++ ;
// printf("%d\t",p[i]);
}
end = time(NULL);
printf( " \nit takes your %f seconds in filter.\n " ,difftime(end,start));
printf( " filter2(end): count = %ld " ,count);
delete[] p;
return count;
}
以上的算法没有实质改进,不过其所用的内存少了一半,在进行比较等操作时循环也只有原来一半,所用的总时间和上面的筛选算法比也只是一半多一点,我觉得最重要的是,申请的内存少了,函数失败可能性变少,而且这个改进是比较有必要的,若任由它作无谓的计算,我会很心痛咯.呵呵.
注:虽然因判定法只用了几个变量,我想声明为寄存器变量,可是你知道的,"尽可能"并不是"一定",何况即使得到了,还不够快.硬件和软件算法的改善都有效果,硬件要成本,算法要技术。怎么办?掠拌.