![](/img/trans.png)
[英]How to get all the prime numbers from 2 to 10^9? [sieve of eratosthenes is not working since the range is too long]
[英]My Sieve of Eratosthenes takes too long
我已经实现了Eratosthenes 筛网以解决SPOJ问题PRIME1 。 尽管输出很好,但是我的提交超出了时间限制。 如何减少运行时间?
int main()
{
vector<int> prime_list;
prime_list.push_back(2);
vector<int>::iterator c;
bool flag=true;
unsigned int m,n;
for(int i=3; i<=32000;i+=2)
{
flag=true;
float s = sqrt(static_cast<float>(i));
for(c=prime_list.begin();c<=prime_list.end();c++)
{
if(*c>s)
break;
if(i%(*c)==0)
{
flag=false;
break;
}
}
if(flag==true)
{
prime_list.push_back(i);
}
}
int t;
cin>>t;
for (int times = 0; times < t; times++)
{
cin>> m >> n;
if (t) cout << endl;
if (m < 2)
m=2;
unsigned int j;
vector<unsigned int> req_list;
for(j=m;j<=n;j++)
{
req_list.push_back(j);
}
vector<unsigned int>::iterator k;
flag=true;
int p=0;
for(j=m;j<=n;j++)
{
flag=true;
float s = sqrt(static_cast<float>(j));
for(c=prime_list.begin();c<=prime_list.end();c++)
{
if((*c)!=j)
{
if((*c)>s)
break;
if(j%(*c)==0)
{
flag=false;
break;
}
}
}
if(flag==false)
{
req_list.erase (req_list.begin()+p);
p--;
}
p++;
}
for(k=req_list.begin();k<req_list.end();k++)
{
cout<<*k;
cout<<endl;
}
}
}
您的代码很慢,因为您没有实现Eratosthenes的Sieve算法。 该算法以这种方式工作:
1) Create an array with size n-1, representing the numbers 2 to n, filling it with boolean values true (true means that the number is prime; do not forget we start counting from number 2 i.e. array[0] is the number 2)
2) Initialize array[0] = false.
3) Current_number = 2;
3) Iterate through the array by increasing the index by Current_number.
4) Search for the first number (except index 0) with true value.
5) Current_number = index + 2;
6) Continue steps 3-5 until search is finished.
该算法花费O(nloglogn)时间。 实际上,您执行的操作会花费更多时间(O(n ^ 2))。 顺便说一句,在第二步中(您搜索n和m之间的质数),您不必检查这些数是否再次为质数,理想情况下,您将在算法的第一阶段对其进行计算。
正如我在该站点中看到的那样,您链接的主要问题是您实际上无法创建大小为n-1的数组,因为最大数量n为10 ^ 9,如果您以这种幼稚的方式进行操作,则会导致内存问题。 这个问题是你的:)
我会扔掉您所拥有的东西,然后从一个非常简单的筛子实施开始,如果确实需要,只会增加更多的复杂性。 这是一个可能的起点:
#include <vector>
#include <iostream>
int main() {
int number = 32000;
std::vector<bool> sieve(number,false);
sieve[0] = true; // Not used for now,
sieve[1] = true; // but you'll probably need these later.
for(int i = 2; i<number; i++) {
if(!sieve[i]) {
std::cout << "\t" << i;
for (int temp = 2*i; temp<number; temp += i)
sieve[temp] = true;
}
}
return 0;
}
对于给定的范围(最多32000),此操作可以在一秒钟之内很好地运行(输出定向到文件-在屏幕上通常会比较慢)。 不过这取决于您...
我不确定您是否实施了Erasthotenes筛子。 无论如何,可以在某种程度上加快算法速度的几件事是:通过预分配空间(查找std::vector<>::reserve
)避免向量内容的多次重分配。 sqrt
操作非常昂贵,您可以通过修改测试来完全避免它(当x*x > y
而不是检查x < sqrt(y)
时停止。
同样,通过修改实际算法,您将获得更好的改进。 从粗略的外观来看,似乎您正在遍历所有候选对象,并且对于每个候选对象,都试图除以可能是因素的所有已知素数。 Erasthotenes的筛网采用一个素数,并在一次通过中丢弃该素数的所有倍数。
请注意,筛子不会执行任何操作来测试数字是否为质数,如果在此之前未将其丢弃则为质数。 对于每个唯一因子,每个非素数仅被访问一次。 另一方面,您的算法正在处理每个数字很多次(相对于现有素数)
我认为稍微加快筛选速度的一种方法是防止在此行中使用mod运算符。
if(i%(*c)==0)
代替(相对)昂贵的mod操作,也许如果您在筛子中向前进行添加迭代。
老实说,我不知道这是否正确。 没有注释并且使用单字母变量名很难阅读您的代码。
我理解问题的方式是必须生成[m,n]范围内的所有素数。
执行此操作而不必从[0,n]计算所有素数的一种方法(因为这很可能使您放慢速度)是首先生成[0,sqrt(n)]范围内的所有素数。
然后使用结果在[m,n]范围内进行筛分。 要生成素数的初始列表,请实现Eratosthenes筛子的基本版本(几乎只有Wikipedia文章中的伪代码的幼稚实现都可以实现)。
这应该使您能够在很短的时间内解决问题。
这是Eratosthenes筛子的简单示例实现:
std::vector<unsigned> sieve( unsigned n ) {
std::vector<bool> v( limit, true ); //Will be used for testing numbers
std::vector<unsigned> p; //Will hold the prime numbers
for( unsigned i = 2; i < n; ++i ) {
if( v[i] ) { //Found a prime number
p.push_back(i); //Stuff it into our list
for( unsigned j = i + i; j < n; j += i ) {
v[i] = false; //Isn't a prime/Is composite
}
}
}
return p;
}
它返回一个仅包含从0到n的质数的向量。 然后,您可以使用它来实现我提到的方法。 现在,我将不为您提供实现,但是,您基本上必须执行与Eratosthenes筛子相同的操作,但是您不必使用所有整数[2,n],而只需使用找到的结果即可。 不知道这是否给了太多?
由于原始问题中的SPOJ问题并未指定必须使用Eratosthenes筛来解决,因此这是基于本文的替代解决方案。 在我六岁的笔记本电脑上,最坏的单个测试案例(nm = 100,000)可以运行约15 ms。
#include <set>
#include <iostream>
using namespace std;
int gcd(int a, int b) {
while (true) {
a = a % b;
if(a == 0)
return b;
b = b % a;
if(b == 0)
return a;
}
}
/**
* Here is Rowland's formula. We define a(1) = 7, and for n >= 2 we set
*
* a(n) = a(n-1) + gcd(n,a(n-1)).
*
* Here "gcd" means the greatest common divisor. So, for example, we find
* a(2) = a(1) + gcd(2,7) = 8. The prime generator is then a(n) - a(n-1),
* the so-called first differences of the original sequence.
*/
void find_primes(int start, int end, set<int>* primes) {
int an; // a(n)
int anm1 = 7; // a(n-1)
int diff;
for (int n = start; n < end; n++) {
an = anm1 + gcd(n, anm1);
diff = an - anm1;
if (diff > 1)
primes->insert(diff);
anm1 = an;
}
}
int main() {
const int end = 100000;
const int start = 2;
set<int> primes;
find_primes(start, end, &primes);
ticks = GetTickCount() - ticks;
cout << "Found " << primes.size() << " primes:" << endl;
set<int>::iterator iter = primes.begin();
for (; iter != primes.end(); ++iter)
cout << *iter << endl;
}
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.