筛选法求素数
时间限制(普通/Java):1000MS/3000MS 运行内存限制:65536KByte
总提交: 48 测试通过: 3
描述
请使用筛选法输出[a, b]之间的所有素数
输入
输入数据有多组,每组数据占一行,每行2个正整数a和b,其中2<=a<=b<=1000000。
输出
每组数据按从小到大的顺序输出[a, b]中所有的素数,每行最多输出10个素数。每组数据之后空一行。
样例输入
2 3 2 50
样例输出
2 3 2 3 5 7 11 13 17 19 23 29 31 37 41 43 47
解题思路:
1.打素数表.
程序采用筛选的思想对指定范围内的所有正整数进行排除,最后得以"幸存"的全部都为质数。该算法思想详见维基百科Sieve of Eratosthenes 相关条目。需要注意的是以上C++实现中的外层循环只用进行到sqrt(upper),证明如下:
假设存在一个非质数n,满足sqrt(upper) < n <= upper且它没有被程序排除,
则n可以写成n = k * t 的形式,这里k与t都是正整数,且k与t中至少有一个数小于sqrt(upper),否则会产生n > upper的矛盾。
不失一般性,假设这个数为t,那么在之前的循环中t已经被程序检查过,这里可以分为两种情况:
1. t不是任意一个小于t的正整数的整倍数
那么在内层循环中我们对所有的t * 2, t * 3, t * 4, ……, t * k, ……做过检查,必然使得数 t * k 被排除,矛盾。
2. 存在一个小于t的最小正整数d,且t是d的整倍数
那么在程序之前的外层循环中t被检查过,d作为t的倍数被排除。对于数 k * t, 它必然也是d的整倍数,同样会被排除,矛盾。
所以程序在外层循环时只需要对所有不大于sqrt(upper)的数进行检查即可,节省了相当一部分时间。
代码:
//#include <cmath>
//#include <fstream>
//#include <iomanip>
#include <iostream>
#include <cstring>
using namespace std;
int primeNum[1000005];
int cnt = 0;
int getDigitNum(int upper)
{
// upper = abs(upper);
int num = 0;
do
{
++num;
upper /= 10;
}while(upper);
return num;
}
void printPrimeUpTo(int upper)
{
int i;
int size = upper - 1;
bool *t = new bool[1000005];
for(i = 0; i != size; ++i)
t[i] = true;
int index = 0, p;
while(true)
{
while(!t[index]) ++index;
p = index + 2;
if(p * p > upper)
break;
int j = 2, mul;
while(true)
{
mul = p * j;
if(mul > upper)
break;
t[mul - 2] = false;
++j;
}
++index;
}
for(i = 0; i != size; ++i)
{
if(t[i])
{
++cnt;
primeNum[cnt] = i+2;
}
}
}
int main()
{
int n , m;
printPrimeUpTo(1000005);
while(cin >> n >> m)
{
int k = 0;
int t = 0;
int flag;
for(int i = 0; i < cnt; ++i)
{
flag = 0;
if(primeNum[i] >= n && primeNum[i] <= m)
{
if(t == 1)
{
cout << " " << primeNum[i];
}
else
{
cout << primeNum[i];
t = 1;
}
k++;
}
if(k == 10)
{
cout << endl;
flag = 1;
k = 0;
t = 0;
}
}
cout << endl;
if(flag != 1)
cout << endl;
}
return 0;
}