ACM: 高速打素数表 TOJ 3749 (疯狂…

 

 

                                                            筛选法求素数 分享至QQ空间

时间限制(普通/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;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值