Спой PRIME1 с использованием сита из эратосфена (в кл) - PullRequest
3 голосов
/ 20 февраля 2012

Я пытаюсь решить проблему 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 ]

2 голосов
/ 20 февраля 2012

Ваша проблема:

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

Вы когда-либо исключаете кратные числа 2 как таковые, если нижний предел диапазона равен 4 или меньше, и вы удаляете только кратные простые числа, большие sqrt(m).Удалите часть i >= m из условия цикла и замените ее на if (i < m) { continue; } в теле цикла (лучше рассчитать первое кратное p не менее чем m напрямую и полностью избежать этого условия).

И вместо использования 't' и 'f' в качестве флагов используйте 1 и 0 в качестве предназначенного 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, и начать внутренний цикл с большего p*p и наименьшегократный p не менее m.Ваш код для последнего работает, так что заверните его в

if ((i = p*p) < m) {
    /* set i to smallest multiple of p which is >= m */
}

(вы можете сделать это немного быстрее, избегая ветвления и используя только одно деление, но разница будет крошечной).

Наконец, ваш выбор того, что вы представляете с помощью 0 и 1, неканоничен, это C, поэтому 0 оценивается как ложное в условиях, а все остальное - как истинное, поэтому каноническая замена была бы 't' -> 1 и 'f' -> 0и в контексте, подобном этому, где записи массива являются флагами, можно проверить

if (flags[p])   // instead of: if (flags[p] != 0)
if (!flags[p])  // instead of: if (flags[p] == 0)

, также нет необходимости изменять типы массивов с char[] на int[], char является целочисленным типомтоже, так что 0 и 1 идеально подходят char значения.Выбор типа для массивов влияет на производительность.С одной стороны, загрузка и сохранение в формате int обычно выполняются быстрее, чем в байтах, поэтому для чтения и записи в формате Word предпочтение отдается int flags[] или даже long int flags[].С другой стороны, с меньшим char flags[] вы получаете лучшую локальность кэша.Вы могли бы получить еще лучшую локальность кэша с использованием одного бита на флаг, но это сделало бы чтение / установку / очистку флагов еще медленнее.То, что дает лучшую производительность, зависит от архитектуры и размера сит.

0 голосов
/ 14 сентября 2012

Даниэль Фишер, похоже, прояснил некоторые основные проблемы.

Если вы хотите увидеть более краткий код / ​​объяснение этой проблемы простых чисел, проверьте:

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

...