Решая вопрос о поиске простого числа в заданном диапазоне, я получаю ошибку Sigsegv и не могу найти, в чем моя ошибка и как ее исправить

#include<iostream>
#include<cmath>
using namespace std;
int primes[10000000];// stores prime upto a max value
int prime[10000000];//stores prime in a given range
int main()
{
  long long int t,m,n,s,k,q;
  for(long long int i=1;i<=1000000;i++){
      primes[i]=1;
      primes[1]=0;
  }

    //stores prime using sieve    
    for(long long int i=2;i<=sqrt(1000000);i++) 
    {
        if(primes[i]==1)
        {
            for(long long int j=2;i*j<=1000000;j++)
            {
                primes[i*j]=0;
            }
        }   
    }
    cin>>t;
    while(t--)
    {
        cin>>m>>n;
        //marking all indices as 1 
        for(long long int i=m;i<=n;i++)      
        {
          prime[i]=1;
        }
        //calculating which offset to mark
        for(long long int i=2;i<=n-m+1;i++)       
        {
            if(primes[i]==1)
            {
                long long int x=(m/i)*i;
                while(x<m)
                x=x+i;   
                for(long long int j=x;j<=n;j=j+i)
                {
                    if(primes[j]==0)
                    prime[j]=0;
               }  
             }
           }         
        for(long long int i=m;i<=n;i++)
        {
            if(prime[i]==1&&i!=1)
            cout<<i<<"\n";
        }
    }
    return 0;
}
0
Beginner 21 Апр 2016 в 16:55

2 ответа

Лучший ответ

Рассмотрим случай:

1
100000000 100000009

Когда я запускал ваш код по ссылке ideone: здесь.

Это выдало ошибку времени выполнения.


Причина: вы инициализировали простой массив размером 107 , но диапазон m, n может достигать 10 9 .

Следовательно, как только встречается prime[i]=1, ваша система дает сбой.

for(long long int i=m;i<=n;i++)      
{
      prime[i]=1;
}

Предложение: Изучите Сито Эратосфена. А поскольку диапазон m, n может быть
1 <= m <= n <= 1000000000, n-m<=100000

Если мы возьмем корень sqrt из 109 , то он приблизится к 31622 . Вот почему мы выбрали массив num размером 32000 (в моем коде) . После этого мы подсчитали количество простых чисел, лежащих в диапазоне 2 - 32000 .

Теперь рассмотрим три случая:

  1. Когда m and n оба меньше 32000. Затем просто используйте вычисленный массив prime и выведите требуемые простые числа.

  2. Когда оба m and n находятся за пределами диапазона 32000, проверьте, является ли число i (в диапазоне от m до n) не делимым любым простым числом (присутствует в массиве prime кода) . Если i не делится ни на одно из чисел, выведите его.

  3. Если диапазон m and n частично меньше 32000 и частично вне 32000. Затем разделите диапазон на две части: одна полностью меньше и равна 32000, а другая полностью больше 32000. И повторите Шаг-1 для первого диапазона и Шаг-2 для второго диапазона.

Ниже мой код, пожалуйста, сочтите его полезным, но не копируйте и не вставляйте его в SPOJ.

#include<stdio.h>
#include<math.h>

int num[32000] = {0},prime[3500],prime_index = -1;

int main() {
    prime[++prime_index] = 2;
    int i,j,k;

    for(i=3;    i<179;     i += 2) {
        if(num[i] == 0) {
            prime[++prime_index] = i;

            for(j = i*i, k = 2*i;    j<=32000;   j += k) {
                num[j] = 1;
            }
        }
    }

    for(;   i<=32000;   i+= 2) {
        if(num[i] == 0) {
            prime[++prime_index] = i;
        }
    }

    int t,m,n;
    scanf("%i",&t);
    while(t--) {
        scanf("%i%i",&m,&n);

        if(m == 1)
            m++;

        if(m == 2 && m <= n) {
            printf("2\n");
        }

        int sqt = sqrt(n) + 1, arr[100005] = {0};

        for(i=0;    i<=prime_index; i++) {
            if(prime[i] > sqt) {
                sqt = i;
                break;
            }
        }

        for(;   m<=n && m <= prime[prime_index];   m++) {
            if(m&1 && num[m] == 0) {
                printf("%i\n",m);
            }
        }

        for(i=0;    i<=sqt;     i++) {
            j = prime[i] * (m / prime[i]);

            if(j < m) {
                j += prime[i];
            }

            for(k=j;    k<=n;   k += prime[i]) {
                arr[k-m] = 1;
            }
        }

        for(i=0;    i<=n-m; i++) {
            if(!arr[i]) {
                printf("%i\n",m+i);
            }
        }

        printf("\n");
    }
    return 0;
}

Любые сомнительные комментарии приветствуются.

0
surajs1n 28 Апр 2016 в 15:27

Компилятор, который вы используете, может не допускать статического распределения больших фрагментов данных, например

int primes[10000000];

Это более 2 ^ 25 байт. Такой большой фрагмент может превышать возможности компилятора или его среды выполнения на SPOJ. Можно было бы new или malloc() такой большой кусок, но этот обходной путь, вероятно, приведет вас в тупик.

Другая проблема заключается в том, что вы читаете m и n из ввода, не проверяя, что они находятся в безопасных пределах. По крайней мере, один из тестовых примеров на SPOJ будет на два порядка выше пределов вашего кода , потому что ваше распределение составляет 10 ^ 7, а предел SPOJ - 10 ^ 9. Это значит, что авария неизбежна.

Вам не нужно полное 32-битное целое число для хранения логического значения; вы можете использовать bool и таким образом сократить требования к памяти на одну четверть. Или вы можете рассматривать каждую ячейку массива размером с байты как упакованное растровое изображение с 8 битами, сокращая использование памяти до 1/32 по сравнению с нынешним. А поскольку вы используете C ++, все уже аккуратно упаковано для вас в виде std::vector<bool> (что скрывает под капотом битовую упаковку).

Примечание: массивы должны быть в сто раз больше, чтобы просеивать все числа вплоть до предела PRIME1, равного 1 000 000 000. Хотя можно просеять все числа в этом диапазоне (ограничение по времени более чем велико - примерно в 10000 раз больше, чем необходимо для этой задачи), это, вероятно, непросто для человека, который совершенно не знаком с программированием.

Однако задача не требует просеивания миллиарда чисел. Он требует просеивания только небольшого количества диапазонов, каждый из которых не превышает 100001 числа. Даже простой неоптимизированный код может сделать это менее чем за миллисекунду, даже с std::vector<bool>, который на порядок медленнее, чем любая разумная структура данных.

Ключевое слово, на которое следует обратить внимание, - «сито Эратосфена с окнами». В Code Review есть сотни тем, касающихся PRIME1. Взгляните.

2
DarthGizka 21 Апр 2016 в 16:33