使用eratosthenes筛子的Spoj PRIME1(在C中)

时间:2012-02-20 13:47:17

标签: c primes sieve-of-eratosthenes

我正在尝试使用Eratosthenes的分段筛来解决问题PRIME1。我的程序可以使用最高NEW_MAX的正常筛子正常工作。但是案例n > NEW_MAX存在一些问题,其中分段筛分发挥作用。在这种情况下,它只打印所有数字。以下是包含相关测试用例的代码的链接:http://ideone.com/8H5lK#view_edit_box

/* segmented sieve */
#include <math.h>
#include <stdio.h>
#include <stdlib.h>
#define MAX_LIMIT 1000000000  //10^9
#define NEW_MAX  31623 /*SQUARE ROOT OF 1000000000*/
#define MAX_WIDTH 100000   //10^5
char flags[NEW_MAX+100];  /*TO PREVENT SEGMENTATION FAULT*/ 

void initialise(char flagarr[], long int n) //initialise all elements to true from 1 to n
{
    long int i;
    for (i = 1; i <= n; i++)
        flagarr[i] = 't';
}

void sieve(unsigned long long m, unsigned long long n, char seg_flags[])
{
    unsigned long long p, i, limit;
    if (m == 1)
        seg_flags[1] = 'f';
    /*Seperate inner loop for p=2 so that evens are not iterated*/
    for (i = 4; i >= m && i <= n; i += 2)
    {
        seg_flags[i-m+1] = 'f';
    }
    if (seg_flags == flags)
        limit = NEW_MAX;
    else
        limit = sqrt(n);
    for (p = 3; p <= limit+1; p += 2)  //initial p+=2 bcoz we need not check even
    {
        if (flags[p] == 't')
        {
            for (i = p*p; i >= m && i <= n; i += p)  //start from p square since under it would have been cut
            seg_flags[i-m+1] = 'f';          /*p*p,  p*p+p,  p*p + 2p   are not primes*/
        }
    }
}

void print_sieve(unsigned long long m,unsigned long long n,char flagarr[])
{
    unsigned long long i;
    if (flags == flagarr)    //print non-segented sieve 
    {
        for (i = m; i <= n; i++)
            if (flagarr[i] == 't')
                printf("%llu\n", i);
    }
    else
    {
        //print segmented
        for (i = m; i <= n; i++)
            if (flagarr[i-m+1] == 't')
                printf("%llu\n", i);
    }
}

int main()
{
    unsigned long long m, n;
    int t;
    char seg_flags[MAX_WIDTH+100];
    /*setting of flags for prime nos. by sieve of erasthromas upto NEW_MAX*/
    initialise(flags, NEW_MAX);
    flags[1] = 'f'; /*1 is not prime*/
    sieve(1, NEW_MAX, flags);
    /*end of initial sieving*/
    scanf("%d", &t);
    while (t--)
    {
        scanf("%llu %llu", &m, &n);
        if (n <= NEW_MAX)
            print_sieve(m, n, flags); //NO SEGMENTED SIEVING NECESSARY
        else if (m > NEW_MAX)
        {
            initialise(seg_flags, n-m+1);  //segmented sieving necessary
            sieve(m, n, seg_flags);
            print_sieve(m, n, seg_flags);
        }
        else if (m <= NEW_MAX && n > NEW_MAX)  //PARTIAL SEGMENTED SIEVING NECESSARY
        {
            print_sieve(m, NEW_MAX, flags);
            /*now lower bound for seg sieving is new_max+1*/
            initialise(seg_flags, n-NEW_MAX);
            sieve(NEW_MAX+1, n, seg_flags);
            print_sieve(NEW_MAX+1, n, seg_flags);
        }
        putchar('\n');
    }
    system("pause");
    return 0;
}

更新:谢谢你回复丹尼尔。我实现了一些你的建议,我的代码现在产生正确的输出: -

/*segmented sieve*/
#include<math.h>
#include<stdio.h>
#include<stdlib.h>
#define MAX_LIMIT 1000000000  /*10^9*/
#define NEW_MAX  31623 /*SQUARE ROOT OF 1000000000*/
#define MAX_WIDTH 100000   /*10^5*/
int flags[NEW_MAX+1];  /*TO PREVENT SEGMENTATION FAULT goblal so initialised to 0,true*/    

void initialise(int flagarr[],long int n) 
/*initialise all elements to true from 1 to n*/
{
    long int i;
    for(i=3;i<=n;i+=2)
        flagarr[i]=0;
}

void sieve(unsigned long m,unsigned long n,int seg_flags[])
{

    unsigned long p,i,limit;  

    /*Seperate inner loop for p=2 so that evens are not iterated*/
    if(m%2==0)
        i=m;
    else
        i=m+1;
    /*i is now next even > m*/
    for(;i<=n;i+=2)
    {

        seg_flags[i-m+1]=1;

    }
    if(seg_flags==flags)
        limit=NEW_MAX;
    else
        limit=sqrt(n);
    for(p=3;p<=limit+1;p+=2)  /*initial p+=2 bcoz we need not check even*/
    {
        if(flags[p]==0)
        {


            for(i=p*p; i<=n ;i+=p)  
            /*start from p square since under it would have been cut*/
            {
                if(i<m)
                    continue;
                seg_flags[i-m+1]=1; 
                     /*p*p,  p*p+p,  p*p + 2p   are not primes*/

            }
        }
    }
}

void print_sieve(unsigned long m,unsigned long n,int flagarr[])
{
    unsigned long i;
    if(m<3)
    {printf("2\n");m=3;}
    if(m%2==0)
        i=m+1;
    else
        i=m;
if(flags==flagarr)    /*print non-segented sieve*/  
{
    for(;i<=n;i+=2)
        if(flagarr[i]==0)
                printf("%lu\n",i);
}
else {
 //print segmented

    for(;i<=n;i+=2)
        if(flagarr[i-m+1]==0)
                printf("%lu\n",i);
}}

int main()
{
    unsigned long m,n;
    int t;
    int seg_flags[MAX_WIDTH+100];
    /*setting of flags for prime nos. by sieve of erasthromas upto NEW_MAX*/
    sieve(1,NEW_MAX,flags);
    /*end of initial sieving*/
    scanf("%d",&t);
    while(t--)
    {
        scanf("%lu %lu",&m,&n);
        if(n<=NEW_MAX)
            print_sieve(m,n,flags); 
            /*NO SEGMENTED SIEVING NECESSARY*/
        else if(m>NEW_MAX)
        {
            initialise(seg_flags,n-m+1);  
            /*segmented sieving necessary*/
            sieve(m,n,seg_flags);
            print_sieve(m,n,seg_flags);
        }
        else if(m<=NEW_MAX && n>NEW_MAX) 
         /*PARTIAL SEGMENTED SIEVING NECESSARY*/
        {
            print_sieve(m,NEW_MAX,flags);
            /*now lower bound for seg sieving is new_max+1*/
            initialise(seg_flags,n-NEW_MAX);
            sieve(NEW_MAX+1,n,seg_flags);
            print_sieve(NEW_MAX+1,n,seg_flags);
        }
        putchar('\n');
    }
    system("pause");
    return 0;
}

但我的筛选功能在下面进一步考虑到你的其他建议会产生不正确的输出: -

void sieve(unsigned long m,unsigned long n,int seg_flags[])
{

        unsigned long p,i,limit;  
        p=sqrt(m);
        while(flags[p]!=0)
                p++;
        /*we thus get the starting prime, the first prime>sqrt(m)*/

        if(seg_flags==flags)
                limit=NEW_MAX;
        else
                limit=sqrt(n);
        for(;p<=limit+1;p++)  /*initial p+=2 bcoz we need not check even*/
        {
                if(flags[p]==0)
                {
                        if(m%p==0) /*to find first multiple of p>=m*/
                                i=m;
                        else
                                i=(m/p+1)*p;

                        for(; i<=n ;i+=p)  
                        /*start from p square since under it would have been cut*/
                        {

                                seg_flags[i-m+1]=1;     
                                         /*p*p,  p*p+p,  p*p + 2p   are not primes*/

                        }
                }
        }
}

2 个答案:

答案 0 :(得分:2)

你的问题是

for (i = 4; i >= m && i <= n; i += 2)
for (i = p*p; i >= m && i <= n; i += p)

如果范围的下限为4或更小,则只消除2的倍数,并且只消除大于sqrt(m)的素数的倍数。从循环条件中移除i >= m部分并将其替换为循环体中的if (i < m) { continue; }(更好的是,直接计算p的第一个倍数不小于m并避免那个条件完全)。

而不是使用't''f'作为标记,而是使用10作为DMR,这将更好理解。

重新更新:此

/*Seperate inner loop for p=2 so that evens are not iterated*/
if(m%2==0)
    i=m;
else
    i=m+1;
/*i is now next even > m*/
for(;i<=n;i+=2)
如果m == 2

会伤到你。如果m == 2,您必须以i = 4开头。

关于

unsigned long p,i,limit;  
p=sqrt(m);
while(flags[p]!=0)
    p++;
/* snip */
for(;p<=limit+1;p++)

似乎你误解了我,“你只消除了大于sqrt(m)的素数倍数”并不意味着你不需要消除多个较小的素数,这意味着你的初始版本没有,导致几乎所有数字都没有消除。您应该使用p = 2启动外部循环 - 或者为2的倍数单独传递,并使用3开始循环,将p递增2,并在{{的较大值处开始内循环1}}和p*p的最小倍数不小于p。你的后者代码有效,所以将它包装在

m

会起作用(你可以让它快一点,避免使用一个分支,只使用一个分区,但差别很小)。

最后,您对0和1所代表的内容的选择是非规范的,这是C,所以0在条件中评估为false,其他一切都为true,因此规范替换将是if ((i = p*p) < m) { /* set i to smallest multiple of p which is >= m */ } 和{{在这样的上下文中,数组条目是标志,可以检查

't' -> 1

也不需要将数组类型从'f' -> 0更改为if (flags[p]) // instead of: if (flags[p] != 0) if (!flags[p]) // instead of: if (flags[p] == 0) char[]也是一个整数类型,因此0和1是非常精细的int[]值。阵列的类型选择具有性能影响。一方面,char大小的加载和存储通常比字节大小更快,因此有利于char甚至int进行字大小的读写操作。另一方面,使用较小的int flags[]可以获得更好的缓存局部性。使用每个标志一位,您将获得更好的缓存局部性,但这将使读取/设置/清除标志仍然更慢。产生最佳性能的因素取决于筛网的结构和尺寸。

答案 1 :(得分:0)

Daniel Fischer似乎澄清了一些主要的问题点。

如果您有兴趣查看有关此素数问题的更简洁的代码/解释,请查看:

http://www.swageroo.com/wordpress/spoj-problem-2-prime-generator-prime1/