网上有线性阶的素数算法,这里不讨论该算法,只是针对平常我们使用的素数判断方法确定素数集合的平方阶算法进行讨论!大神勿笑!
首先是素数的判断方法,我相信大家一般对以下的代码再熟悉不过了:
bool isPrime( int source ) { //判断参数是否为素数
int source_sqrt = sqrt( source );
for(int i = 2; i <= source_sqrt; ++i)
if( source % i == 0 )
return false;
return true;
}
这个算法就是对所判断的数开平方根,并得到一个中间数,而此中间数的结果作为判断原数的最大因数。这个算法毋庸置疑是线性阶的!
然后是给定一个值,确定大于0、小于等于该值的范围内的素数集合。有了以上判断素数的方法,要想写一个这样的算法,根本没有任何问题:
void Prime_field_low( int limited, vector<int>& out ) {
//对(0, limited]这个区间确定有多少素数,完全是平方阶,而且没有优化
for( ; limited > 0; --limited )
if( limited & 1 == 0 && limited != 2 )
continue;
else if( isPrime( limited ) )
out.push_back( limited );
}
但是问题来了,首先当前的算法就是线性阶了,还去调用另一个线性阶,在线性阶的层次上再次累加线性阶,也就构成了平方阶,这个算法中隐藏的最大几个问题就是:
1、对一个数的因数遍历了很多不该遍历的合数,这些合数一定可以排除在外,最明显的就是偶数,以上的算法中虽然continue过去了,但依然进行了不必要的判断;
2、我们知道任何数都是由素数的乘积组成,因而如果我们考虑一个容器,该容器中装的是小于当前值的素数,那么对于小于当前值开平方根的中间数的任何素数进行取余判断,我们就可以无形之中砍掉很多多余的判断,从而优化这个复杂的判断过程!
既然可以优化,那么我们何不写一个优化算法来呢?
void Prime_field_high( int limited, vector<int>& out ) {
//任何数都是素数的乘积,对奇数开平方根的结果至3这个范围内的每个素数进行遍历判断
if( limited > 3 ) { //虽是平方阶,但是经过了优化
out.push_back( 1 );
out.push_back( 2 );
out.push_back( 3 );
}
else {
Prime_field_low( limited, out );
return;
}
for( int i = 5; i <= limited; i += 2 ) {
int index = out.size(), i_sqrt = sqrt( i );
for( int j = 2; j < index; ++j )
if( out[j] <= i_sqrt )
if( i % out[j] == 0 )
break;
else
continue;
else {
out.push_back( i );
break;
}
}
}
以上代码简明易懂,用到了STL中的vector容器。最后给出整个测试程序和测试结果:
#include<iostream>
#include<vector>
#include<cmath>
#include<iomanip>
#include<stdlib.h>
#include <sys/time.h>
using namespace std;
const int max_int = ( 1 << 24 ) - 1; //此处可以修改需要测试的数据量大小,现在是16 777 215
bool isPrime( int source ) { //判断参数是否为素数
int source_sqrt = sqrt( source );
for(int i = 2; i <= source_sqrt; ++i)
if( source % i == 0 )
return false;
return true;
}
void Prime_field_low( int limited, vector<int>& out ) {
//对(0, limited]这个区间确定有多少素数,完全是平方阶,而且没有优化
for( ; limited > 0; --limited )
if( limited & 1 == 0 )
continue;
else if( isPrime( limited ) )
out.push_back( limited );
}
void Prime_field_high( int limited, vector<int>& out ) {
//任何数都是素数的乘积,对奇数开平方根的结果至3这个范围内的每个素数进行遍历判断
if( limited > 3 ) { //虽是平方阶,但是经过了优化
out.push_back( 1 );
out.push_back( 2 );
out.push_back( 3 );
}
else {
Prime_field_low( limited, out );
return;
}
for( int i = 5; i <= limited; i += 2 ) {
int index = out.size(), i_sqrt = sqrt( i );
for( int j = 2; j < index; ++j )
if( out[j] <= i_sqrt )
if( i % out[j] == 0 )
break;
else
continue;
else {
out.push_back( i );
break;
}
}
}
int main() {
timeval tv;
vector<int> vl;
int i = 0;
srand( time( NULL ) );
gettimeofday( &tv, NULL );
double cl = tv.tv_sec * 1000 + (double)tv.tv_usec / 1000, dl;
Prime_field_low( max_int, vl );
gettimeofday(&tv, NULL);
dl = ( tv.tv_sec * 1000 + (double)tv.tv_usec / 1000 );
cout << dl << "-" << cl << " = " << dl - cl << "ms" << endl;
//如果所要测试的数据不多,可以将容器中最后得到的数据打印出来
/*for( ; i < vl.size(); ++i )
cout << setw(10) << vl[i];*/
cout << endl;
vl.clear();
gettimeofday( &tv, NULL );
cl = tv.tv_sec * 1000 + (double)tv.tv_usec / 1000;
Prime_field_high( max_int, vl );
gettimeofday( &tv, NULL );
dl = ( tv.tv_sec * 1000 + (double)tv.tv_usec / 1000 );
cout << dl << "-" << cl << " = " << dl - cl << "ms" << endl;
/*for( i = 0; i < vl.size(); ++i )
cout << setw(10) << vl[i];*/
return 0;
}
这个优化算法的思维我想了一上午,大神们应该也有类同的实现,勿笑,新人学习不易!