Уменьшение использования памяти при проектировании сита эратосфенов в C

Я пытаюсь спроектировать сито эратосфенов на C, но столкнулся с двумя странными проблемами, которые не могу понять. Вот мой основной план программы. Попросите пользователей установить диапазон для отображения простых чисел. Если минимум диапазона ниже 9, установите минимум 9. Заполните массив всеми нечетными числами в диапазоне.

1) Я пытаюсь уменьшить использование памяти, объявляя массивы переменного размера следующим образом:

    if (max<=UINT_MAX)
       unsigned int range[(max-min)/2];
    else if (max<=ULONG_MAX)
         unsigned long int range[(max-min)/2];
    else if (max<=ULLONG_MAX)
         unsigned long long int range[(max-min)/2];

Почему это не компилируется? Переменные min и max объявлены ранее как ints, и в них включен limit.h. Я закомментировал структуру выбора и только что объявил unsigned long long int range[(max-min)/2];, который сейчас компилируется и работает.

2) Мой код работает, но иногда он помечает маленькие простые числа как не простые.

#include<stdio.h>
#include<limits.h>

void prime(int min, int max)
{
    int i, f=0;
    //declare variable size array
    /*if (max<=(int)UINT_MAX)
       unsigned int range[(max-min)/2];
   else if (max<=(int)ULONG_MAX)
         unsigned long int range[(max-min)/2];
    else if (max<=(int)ULLONG_MAX)*/
         unsigned long long int range[(max-min)/2];
    //fill array with all odd numbers
    if (min%2==0)
    {
        for (i=min+1;i<=max;i+=2)
        {
            range[f]=i;
            f+=1;
        }
    }
    else
    {
        for (i=min;i<=max;i+=2)
        {
            range[f]=i;
            f+=1;
        }
    }
    //assign 0 to cell if divisible by any number other than itself
    for (i=3;i<=sqrt(max);++i)
    {
        for (f=0;f<=((max-min)/2);f++)
        {
            if (range[f]%i==0 && f!=i)
                range[f]=0;
        }
    }
    //troubleshoot only: print full range
    for (f=0;f<=((max-min)/2);f++)
    {
            printf("ALL: %d / %d\n", f, range[f]);
    }    
    //display all primes
    if (min==9) /*print primes lower than 9 for ranges where min<9*/
            printf("2\n3\n5\n7\n");
    for (f=0;f<=((max-min)/2);f++) /*print non 0 numbers in array*/
    {
        if (range[f]!=0)
            printf("%d\n", range[f]);
    }
}
int main(void)
{
    int digits1, digits2;
    printf("\n\n\nCalculate Prime Numbers\n");
    printf("This program will display all prime numbers in a given range. \nPlease set the range.\n");
    printf("Minimum: ");
    scanf("%d", &digits1);
    if (digits1<9)
       digits1=9;
    printf("Maximum: ");
    scanf("%d", &digits2);
    printf("Calculating...");
    printf("All prime numbers between %d and %d are:\n", digits1, digits2);
    prime(digits1, digits2);
    getchar();
    getchar();
}

Например, если цифры = 1 и цифры 2 = 200, моя программа выводит все простые числа от 1 до 200, кроме 11 и 13. 11 и 13 отсеиваются, и я не могу понять, почему это происходит со все большим количеством младших чисел, поскольку цифры вырос.

3) Наконец, является ли мое сито правильным ситом Эратосфена? Это работает, но я чувствую, что есть более эффективный способ отсеивания не простых чисел, но я не могу понять, как его реализовать. Одна из моих целей в этой программе — сделать ее максимально эффективной. Опять же, что у меня есть прямо сейчас:

    //assign 0 to cell if divisible by any number other than itself
    for (i=3;i<=sqrt(max);++i)
    {
        for (f=0;f<=((max-min)/2);f++)
        {
            if (range[f]%i==0 && f!=i)
                range[f]=0;
        }
    }

Спасибо, что прочитали все это! Прошу прощения за публикацию еще одного вопроса, связанного с ситом эратосфена, и заранее благодарю вас за помощь!


person Daniel Ong    schedule 26.11.2013    source источник
comment
1. У вас не может быть нескольких объявлений для одной переменной, как вы пытаетесь сделать; это просто не разрешено. Чтобы еще больше сократить использование памяти, обрабатывайте массив как растровое изображение и записывайте в карту только нечетные числа.   -  person Jonathan Leffler    schedule 27.11.2013


Ответы (2)


Нет, это не настоящее сито Эратосфена. Алгоритм решета Эратосфена не проверяет остатки, В Википедии все ясно по этому поводу Я думаю. :) Весь смысл в том, чтобы избежать пробных делений, получить праймы бесплатно, без тестирования.

Как? Путем генерирования их множителей из каждого простого числа, которое мы идентифицируем, в порядке возрастания одно за другим.

Кратность простого числа p: 2p, 2p + p, 2p + p + p, ...

Нечетные кратные простого числа p: 3p, 3p + 2p, 3p + 2p + 2p, ...

По мере их перечисления мы помечаем их в массиве сита. Некоторые будут отмечены дважды или более, например. 15 будет помечено как 3 и как 5 (поскольку 3 * 5 == 5 * 3). Таким образом, мы можем начать перечисление и маркировку с p2:

  for( i=3; i*i < n; i += 2 )
      if( !sieve[i] )         // if `i` is not marked as composite
          for( j = i*i; j < n; j += 2*i )
          {
              sieve[j] = 1;   // 1 for composite, initially all are 0s
          }

Ключ к решету заключается в следующем: мы не храним числа в массиве. Это не массив INTs; это массив 1-битных флагов со значением 0 или 1. Индекс записи в массиве решета означает число, для которого решето сохраняет свой статус: отмеченный, т. е. составной, или еще не отмеченный, т. е. потенциально простой.

Итак, в конце концов, все неотмеченные записи означают простые числа. Конечно, вам нужно будет разработать схему адресации, например. запись с индексом i может соответствовать числу a + 2*i, где a — нечетное начало диапазона. Поскольку ваш диапазон начинается с некоторого смещения, эта схема известна как решето со смещением Эратосфена. Каркас реализации C находится здесь.

Чтобы свести к минимуму использование памяти, нам нужно рассматривать наш массив как битовый массив. В С++, например. это легко: мы объявляем его как vector<bool> и он автоматически бит-упаковывается для нас. В C нам придется сделать небольшую упаковку и распаковку самостоятельно.

Совет: не экономьте на промежуточных переменных. Назовите каждый значимый объект в вашей программе. В вашем коде не должно быть (max-min)/2; но вместо этого определите width = max - min и используйте это имя. Оставьте оптимизацию в малом компилятору. :)


На ваш первый вопрос: дело в области. Ваш код эквивалентен

if (max<=UINT_MAX)
    {   unsigned int range[(max-min)/2]; }    // note the curly braces!
else if (max<=ULONG_MAX)
    {   unsigned long int range[(max-min)/2]; }
else if (max<=ULLONG_MAX)
    {   unsigned long long int range[(max-min)/2]; }

поэтому здесь есть три объявления массива range, каждое в своей области внутри соответствующего блока. Каждая создается при входе в окружающий ее блок ({) и уничтожается при выходе из него (}). Другими словами, он больше не существует для остальной части вашей функции prime. На практике это означает, что если вы объявите свою переменную внутри блока if, вы сможете использовать ее только внутри этого блока (между соответствующими фигурными скобками { и }).

person Will Ness    schedule 27.11.2013

Q1: вы не можете объявить символ (здесь: range) дважды в одной и той же области видимости. Это не совсем ваша проблема, но вы пытаетесь это сделать: вы объявляете range внутри области if, и она не видна снаружи.

person ensc    schedule 26.11.2013