求10的10次方以内的所有素数
朋友应聘一家公司的程序员,问卷中有一道题是求10的10次方以内的素数,要求运行时间不超过10个小时(可以不输出)。想了一天写出来的算法在我1.73G的本本上运行,10个小时内出不来……后来老吴在CSDN上发了贴,很多人回复,其中有一个算法真的很强,如果数字小的话,如1000000内的素数,在我本本上只需140毫秒。。。真的很打击人……他的这段代码是牺牲空间来满足时间,这样就会有一个很致命的弱点,万一内存不够就会很慢很慢,大部分时间都用在页面交换上,因为里面定义了一个char类型的数组作为标志位。显然,10的10次方的数组会占用10的10次方个字节,也就是需要分配10G的内存。天啊!!哪来那么大的内存!所以如果不优化的话就会花大量的时间在与虚拟内存的页面交换上。
后来用了几个小时(工作效率很低……)优化。标志位其实只需1位即可,char类型中的其它7位就会很浪费!只需把这7位都利用上就可以把内存需求降至10/8 = 1.25G。第二步,很显然,除了2以外的所有偶数都不是素数,这样就又可以排除一半的空间需求,最终降到只需625MB内存。只要内存足够,不必用到硬盘上的虚拟内存的话就会速度惊人!在我本本1.73G CPU,1G内存之下计算(没有输出),只用了46分钟!!!这是运行时的输出结果:
*** Calculate all the Primes that is less than 10000000000 ***
Program Started at: 2006.9.1 1:57:5
The total number is: 455052511
Elapsed Time = 2761781 ms
Program Ended at: 2006.9.1 2:43:7
当然,万一内存不够,如只有512MB内存时,运行时间就会相差甚远。
这是在网上找的一个算法,在此基础上优化好的代码如下:
#include <iostream>
#include <complex>
#include <windows.h>
using namespace std;
#define MAX 10000000000 //查找上限
// 一个字节的每一位作为一个数是否素数的标志位
// 除表示去掉偶数,因为除了以外任何偶数都不是素数
char shu[(MAX+1)/8/2+1]; //标志位数组,占用空间较大,已优化,节省空间
const byte bit[8] = { 0x01,
0x02,
0x04,
0x08,
0x10,
0x20,
0x40,
0x80 };
void main(void)
{
// 输出程序开始的时间
cout << " *** Calculate all the Primes that is less than " << MAX << " ***" << endl;
SYSTEMTIME beginTime, endTime;
GetLocalTime( &beginTime );
cout << "Program Started at: " << beginTime.wYear << "." << beginTime.wMonth << "." << beginTime.wDay << " "
<< beginTime.wHour << ":" << beginTime.wMinute << ":" << beginTime.wSecond << endl;
DWORD elapse = GetTickCount();
//=================== 开始计算 ==================
__int64 i = 0;
__int64 notPrime = 0;
__int64 step = 0;
__int64 num = MAX/2; // 记录素数个数
shu[0] = shu[0] | bit[0]; // s[0]代表的是,不是素数
num++; // 2 是素数,但没被列入数组中,此处加
for(i = 0; i < MAX / 4; i++)
{
int tmp1 = i / 8; // 第tmp1个数组
int tmp2 = i % 8; // 第tmp2位
if(shu[tmp1] & bit[tmp2])
continue;
step = 2*i+1;
for(notPrime = step + i; notPrime <= MAX/2; notPrime += step)
{
int tmp1 = notPrime / 8; // 第tmp1个数组
int tmp2 = notPrime % 8; // 第tmp2位
if(!(shu[tmp1] & bit[tmp2])) // 不输出时用来计算有多少个素数
num--;
shu[tmp1] = shu[tmp1] | bit[tmp2];
}
}
// 输出结果,不需要输出则加注释
//printf("2\n");
//for(i = 1, notPrime = 1; i < MAX/2; i++)
//{
// int tmp1 = i / 8; // 第tmp1个数组
// int tmp2 = i % 8; // 第tmp2位
// if(!(shu[tmp1] & bit[tmp2]))
// {
// printf("%d\n", 2*i+1);
// notPrime++; // 输出时计算有多少个素数
// }
//}
printf("The total number is: %d\n", num);
//=================== 结束计算 ==================
// 输出程序结束时的时间,并输出计算运行时间
cout << "Elapsed Time = " << GetTickCount() - elapse << " ms" << endl;
GetLocalTime( &endTime );
cout << "Program Ended at: " << endTime.wYear << "." << endTime.wMonth << "." << endTime.wDay << " "
<< endTime.wHour << ":" << endTime.wMinute << ":" << endTime.wSecond << endl;
FILE* file;
if ( file = fopen( "log.txt", "w" ) )
{
char buffer[512];
sprintf( buffer, "Program Started at: %d.%d.%d %d:%d:%d\n\n",
beginTime.wYear, beginTime.wMonth, beginTime.wDay,
beginTime.wHour, beginTime.wMinute, beginTime.wSecond );
fputs( buffer, file );
sprintf( buffer, "Program Ended at: %d.%d.%d %d:%d:%d\n\n",
endTime.wYear, endTime.wMonth, endTime.wDay,
endTime.wHour, endTime.wMinute, endTime.wSecond );
fputs( buffer, file );
sprintf( buffer, "Elapsed Time = %d ms", GetTickCount() - elapse );
fputs( buffer, file );
fclose( file );
}
// 为了避免程序运行完自动关闭控制台窗口
char a;
while ( true )
cin>>a;
}