Ошибка сегментации, когда мой массив слишком большой - PullRequest
0 голосов
/ 24 февраля 2020

Редактировать: похоже, что просто 9,999,999,999,999 слишком большое число для массива.

Я получаю эту ошибку "программа получила ошибку сегментации sigsegv" в моем коде.

В основном мой код должен выполнять целочисленную факторизацию, и упражнение можно увидеть на codeabbey здесь . В основном я получу входные данные типа 1000 и выведу их как произведение их факторов, в данном случае 2 * 2 * 2 * 5 * 5 * 5.

Я делаю вышеизложенное, имея вектор простых чисел, который я генерирую, используя метод Сита Эратосфена.

Согласно веб-сайту, количество цифр ввода не будет превышать 13, следовательно, мое самое большое число составляет 9 999 999 999 999. Ниже приведен мой код.

#include <iostream>
#include <vector>
#include <cstring>

unsigned long long int MAX_LIMIT = 9999999999999;


std::vector<unsigned long long int> intFactorisation (unsigned long long int num) {
    std::vector<unsigned long long int> answers;
    static std::vector<unsigned long long int> primes;
    if (primes.empty()) {               // generate prime numbers using sieve method
        bool *arr = new bool[MAX_LIMIT];
        memset (arr, true, MAX_LIMIT);
        for (unsigned long long int x = 2; x*x < MAX_LIMIT; x++) {
            if (arr[x] == true) {
                for (unsigned long long int y = x*x; y < MAX_LIMIT; y += x) {

                    arr[y] = false;  // THIS LINE ALWAYS HAS AN ERROR!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!

                }
            }
        }
        for (unsigned long long int x = 2; x <= MAX_LIMIT; x++) {
            if (arr[x]) {
                primes.push_back(x);
            }
        }
    }
    std::vector<unsigned long long int>::iterator it = primes.begin();  // start the factorisation
    while(it != primes.end()) {
        if (num % *it == 0) {
            answers.push_back(*it);
            num/=*it;
        }
        else {
            it++;
        }
        if (num == 1) {
            break;
        }
    }

    return answers;
}


int main() {


    int maxi;
    std::cin >> maxi;
    int out[maxi];
    for (int x = 0; x < maxi; x++) {
        std::cin >> out[x];
    }

    for (auto it : out) {
        std::vector<unsigned long long int> temp = intFactorisation(it);
        for (std::vector<unsigned long long int>::iterator it = temp.begin();
            it != temp.end(); it++) {
            if (it == temp.end() - 1) {
                std::cout << *it << " ";
            }
            else {
                std::cout << *it << "*";
            }
        }
    }

}

Однако по какой-то причине программа всегда завершается при arr [y] = false в функции intFactorisation. При использовании CodeBlocks в левом нижнем углу экрана появится сообщение об ошибке сегментации.

Я уже использовал 'new' в моем смехотворно большом массиве, поэтому память должна быть в куче. Я попытался использовать меньший MAX_LIMIT, например, 100 000, и моя функция работает. Кто-нибудь знает почему?

Кроме того, мне интересно, почему мне не нужно разыменовывать мой указатель обр. Например, arr [y] = false работает, но * arr [y] или (* arr) [y] нет. Надеюсь, это тоже можно уточнить.

Спасибо, что прочитали это, и я ценю любую помощь.

Ответы [ 2 ]

1 голос
/ 27 февраля 2020

Существует два типа проблем в размещенном коде: управление памятью и алгоритмы.

Получение ресурсов

Программа демонстрирует три вида распределения памяти:

  • Массив переменной длины . В main, out объявляется как int out[maxi], maxi как переменная, а не постоянная времени компиляции. Это функция C99, она никогда не была частью какого-либо стандарта C ++, даже если она предлагается в качестве функции некоторым компилятором.

  • bool *arr = new bool[MAX_LIMIT];. Современные рекомендации предлагают избегать использования «голого» * ​​1022 * для выделения памяти и предпочитать умные указатели, стандартные контейнеры и в целом следовать идиоме RAII , но это даже не главная проблема. MAX_LIMIT слишком велик, чтобы не привести к исключению std::bad_alloc, а также delete никогда не вызывается.

    В той же функции есть также al oop, который в конечном итоге получит доступ (маловероятно) выделенная память за пределами: for (unsigned long long int x = 2; x <= MAX_LIMIT; x++) { if (arr[x]) {, x в конечном итоге станет MAX_LIMIT, но до этого у вас не хватит памяти.

  • std::vector. Это было бы так, если бы только программа не пыталась заполнить вектор primes всеми простыми числами до MAX_LIMIT, что составляет 10 13 -1 или почти 80 ТБ при условии 64- битовый тип.

Алгоритм

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

Представьте, что вы попробовали все простые числа до этого квадрата root (назовем это S) и, следовательно, поделили исходное число на любой найденный коэффициент. Если остаток еще есть, это значение не делится ни на одно из упомянутых простых чисел и меньше или равно исходному числу. Это должно быть простое число. Все возможные факторы <= S уже были исключены, и поэтому любой гипотетический фактор> S (что будет результатом деления им? Другого уже проверенного простого числа меньше, чем S).

С точки зрения дизайна, я бы также рассмотрел, как простые числа рассчитываются и хранятся в коде OP. Функция факторизации в основном записывается следующим образом:

unsigned long long int MAX_LIMIT = 9999999999999;

std::vector<unsigned long long int> intFactorisation (unsigned long long int num)
{
    static std::vector<unsigned long long int> primes;
//  ^^^^^^                                           ^
    if (primes.empty())
    {
        // generate prime numbers up to MAX_LIMIT using sieve method
    }
    // Use the primes...
}     

Вектор stati c мог быть инициализирован с использованием функции separete и также объявлен const, но с учетом тесной связи с функцией факторизации может быть лучше обернуть эти данные и функциональные возможности в класс, отвечающий за правильное распределение и инициализацию ресурсов.

С введением лямбда-выражений в язык мы можем избежать большей части шаблонов, связанных с обычным функтором класс, так что функция факторизации может быть построена как лямбда с состоянием, возвращаемая следующим образом:

auto make_factorizer(uint64_t max_value)
{
    uint64_t sqrt_of_max = std::ceil(std::sqrt(max_value));
    // Assuming to have a function returning a vector of primes.
    // The returned lambda will accept the number to be factorized and
    // a reference to a vector where the factors will be stored
    return [primes = all_primes_up_to(sqrt_of_max)](uint64_t number,
                                                    std::vector<uint64_t>& factors)
    {
        uint64_t n{number};
        factors.clear();
        // Test against all the known primes, in ascending order
        for (auto prime : primes)
        {
            // You can stop earlier, if prime >= sqrt(n)
            if (n / prime <= prime)
                break;
            // Keep "consuming" the number with the same prime,
            // a factor can have a multiplicity greater than one.
            while (n % prime == 0)
            {
                n /= prime;
                factors.push_back(prime);
            }
        }
        // If n == 1, it has already found and stored all the factors. 
        // If it has run out of primes or breaked out from the loop,
        // either what remains is a prime or number was <= 1. 
        if (n != 1  ||  n == number)
        {
            factors.push_back(n);
        }
    };
}

Здесь тестируется живая реализация.

0 голосов
/ 24 февраля 2020

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

Вот функция, позволяющая найти фактор без выделяя большую память,

std::vector<unsigned long long int> intFactorisation(unsigned long long int num) {
    std::vector<unsigned long long int> answers{};
    unsigned long long int Current_Prime = 2;
    bool  found;
    while (num!=1 && num!=0)
    {
        //push all the factor repeatedly until it is not divisible
        // for input 12 push 2,2 and exit
        while (num % Current_Prime == 0) {
            answers.push_back(Current_Prime);
            num /= Current_Prime;
        }
        //find Next Prime factor 
        while (Current_Prime <= num) {
            Current_Prime++;
            found = true;
            for (unsigned long long int x = 2; x*x < Current_Prime; x++)
            {
                if (Current_Prime%x == 0) {
                    found = false;
                    break;
                }
            }

            if (found == true)
                break;
        }
    }

    return answers;
}
...