Доступ к неинициализированному значению, скорее всего, в векторе

#c #vector

#c #вектор

Вопрос:

Я выполняю упражнение, в котором мне нужно найти положительные целые p числа , q которые являются множителями другого натурального числа n . Следуя формуле n=pq*q , где p — бесквадратичное число.

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

Логика, которую я попробовал, заключается в следующем. Во-первых, я взял число, которое необходимо учесть (назовите его n). Затем я нашел все множители числа n и поместил их в вектор. После этого проверьте, является ли каждый элемент этого вектора бесквадратным. Если true, поместите элемент в другой вектор (вектор бесквадратичных множителей числа n). После этого пройдите по каждому элементу вектора бесквадратных множителей и решите уравнение q=sqrt(n/p) , где p — бесквадратный множитель из вектора. Кроме того, я проверяю условие if(int(sqrt(n/p))==sqrt(n/p)) , потому что квадратный корень должен быть положительным целым числом.

 #include <iostream>
#include <vector>
#include <cmath>
using namespace std;
// Function that checks if the number is squarefree

bool isSquareFree(int n)
{
    if (n % 2 == 0)
        n = n / 2;
    if (n % 2 == 0)
        return false;
    for (int i = 3; i <= sqrt(n); i  = 2)
    {
        if (n % i == 0)
        {
            n = n / i;
            if (n % i == 0)
                return false;
        }
    }
    return true;
}

void Factorise_the_number(int n, int amp;p, int amp;q)
{
    if (n <= 0)
        return 0;
    vector<int> factors(0); // vector of factors
    vector<int> sqfree_factors(0); // vector of squarefree factors
    int sqfree_number; // the number "p"
    int squared; // is essentially the number "q"

    for (int i = 1; i <= n / 2; i  )
    {
        if (n % i == 0)
            factors.push_back(i); // takes all factors of the number "n"
    }

    for (int i = 0; i < factors.size(); i  )
    {
        if (isSquareFree(factors.at(i)))
            sqfree_factors.push_back(factors.at(i));
    } // checks if each factor is squarefree. if yes, put it in a separate vector

    for (auto x : sqfree_factors)
    {

        if (int(sqrt(n / x)) == sqrt(n / x))
        { // if true, we found the numbers
            squared = sqrt(n / x);
            sqfree_number = x;
            break;
        }
    }

    p = sqfree_number;
    q = squared;
}

int main()
{

    int n, p = 0, q = 0;
    cin >> n;

    Factorise_the_number(n, p, q);
    cout << p << " " << q;

    return 0;
}
 

Например, моя программа работает, если я ввожу номер 99 , но не работает, если я ввожу 39 . Кто-нибудь может дать какое-либо представление?

Спасибо!

Комментарии:

1. Несвязанный: return 0; выглядит странно в возвращаемой функции void .

2. Также не связано: вместо выражения нескольких возвращаемых значений в качестве ссылочных аргументов я обычно предпочитаю, чтобы функция возвращала структуру с возвращаемыми значениями. Это упрощает работу вызывающей стороны: auto pq = Factorise_the_number(n); в отличие от int p; int q; Factorise_the_number(n, p, q);

3. Вместо int n, p=0, q=0; cin >> n; , используйте int n=99, p=0, q=0; Now, людям не нужно вводить n . Они могут продолжать тестировать один и тот же случай снова и снова, пока он не будет взломан. Быстрее и меньше шансов потратить время на случайную отладку опечатки.

4. @user4581301 на самом деле попробуйте ввести 10 , 9223 , 57683 . Для них это тоже не работает. Я только что узнал об этом. Это работает для следующего: 77777, 48, 22360800 Кстати, спасибо за вашу помощь

5. Если он никогда не входит в цикл, потому что sqfree_factors пуст, тогда он будет использовать неинициализированные значения.

Ответ №1:

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

Давайте посмотрим на это вместе. Сначала он пытается найти все факторы и находит 1, 3 и 13: это выглядит нормально.

Затем он проверяет, является ли каждое из этих чисел бесквадратным, и все они таковы: это также выглядит правильно.

Затем он проверяет, удовлетворяет ли какой-либо из факторов без квадрата искомому равенству. Ни один из них этого не делает (39 равно 3 x 13, он никак не может содержать коэффициент в квадрате). Это означает, что if (int(sqrt(n / x)) == sqrt(n / x)) это никогда не бывает истинным, и этот блок никогда не выполняется. Какое значение sqfree_number и squared в этот момент? Он никогда не инициализируется. Использование неинициализированных значений приводит к «неопределенному поведению», то есть ваша программа может делать что угодно. В этом случае p и q в конечном итоге содержит случайные значения.

Как вы можете это исправить? Подумайте об этом: если n не удовлетворяет вашему уравнению, то есть оно не может быть выражено как pq * q, что именно должна выводить программа? Будет ли ваш вывод, как сейчас, когда-либо иметь смысл? Нет. Это означает, что вы должны изменить свою программу таким образом, чтобы она охватывала случай, который вы не рассматривали.

Один из способов — добавить a bool found = false; непосредственно перед вашим последним for циклом. Когда вы найдете факторы, перед разрывом установите для этой переменной значение true . Затем, вне цикла, проверьте это: так ли это true ? Затем вы можете вернуть правильные значения. Но если это все равно false , это означает, что равенство не выполняется, и вы не можете вернуть правильные значения. Вы должны найти способ сообщить об этом вызывающей стороне (которая является вашей main функцией), чтобы она могла напечатать соответствующее сообщение.

И как вы можете это сигнализировать? В общем, вы могли бы изменить свой Factorise_the_number (кстати, имя функций должно начинаться со строчной буквы; для классов обычно используются прописные буквы), чтобы вернуть a bool . Или вы можете использовать хитрость: вернуть специальное значение для p и q , которое не может быть результатом вычисления. Например, -1. Затем, перед печатью, проверьте: если значения равны -1, это означает, что число не может быть выражено как pq * q.

Комментарии:

1. Большое спасибо за вашу помощь! Я смог решить эту проблему после того, как вы указали мне правильное направление. Могу я спросить еще одну вещь, считаете ли вы, что этот алгоритм слишком неэффективен? Потому что в упражнении говорится, что существует много способов выполнить такое разложение на множители (одним из которых может быть поиск всех простых множителей числа n , но должно быть еще много алгоритмов)?

2. Более быстрым подходом было бы изменить порядок ваших проверок с «сначала в ширину» на «сначала в глубину». Прямо сейчас вы находите все факторы, затем проверяете все из них на бесквадратность, а затем пытаетесь установить равенство для всех из них. Представьте, что коэффициенты равны 1000, а 500 не имеют квадрата. Из них третий удовлетворяет равенству. Это означает, что сотни проверок были бесполезны! Вместо этого, когда вы находите фактор, немедленно проверьте, является ли он бесквадратным, и если это так, проверьте равенство. Это быстрее, и вы также экономите память, потому что нет необходимости сохранять ваши результаты.

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