Ошибка SIGABRT (сигнал 6) при поиске мажоритарного элемента в массиве с использованием функции «разделяй и властвуй»

Я проверил SO и увидел две проблемы, связанные с одной и той же постановкой проблемы. Однако ни один из них не содержал того, что я ищу. Задача состоит в том, чтобы вывести 1, если массив из n элементов содержит элемент, который встречается более n/2 раз, используя стратегию разделяй и властвуй.

Я разработал то, что я считаю рабочим решением, основанным на том факте, что базовый вариант представляет собой подмассив с одним элементом, который (очевидно) является основным элементом в подмассиве. Затем я перехожу к сравнению этих элементов большинства в подмассивах, задаюсь вопросом, встречаются ли они в конечном счете более чем n/2 раз.

Для получения дополнительной информации см. https://classes.soe.ucsc.edu/cmps102/Fall01/solutions4.pdf (задача 4)

Я написал два разных решения этой проблемы, одно с использованием очень наивного алгоритма O (n ^ 2), чтобы проверить, правильно ли мое решение для всех случаев, и одно, пытающееся реализовать алгоритм, описанный в ссылке выше.

Внутри main я проверяю свое решение на соответствие очевидно правильному, но наивному. Однако при запуске я получаю ошибку SIGABRT (сигнал 6). Мой отладчик говорит мне проверить наличие malloc, объект, вероятно, был изменен после освобождения.

Теперь я не могу сказать, верно ли мое решение. Я действительно не знаю, что происходит с плохим распределением, я относительно новичок в C++.

Код идет ниже:

#include <algorithm>
#include <iostream>
#include <vector>

using std::vector;

int get_majority_element(vector<int> &a, int left, int right) {


    int m;
    int majority_left, majority_right;      // majority element in either sub array
    int count_right = 0, count_left = 0;    // count for above
    int leftt, rightt;                      // endpoints

    if (a.size() == 1) {
        return a[0];
    }
    else {

        m = (left + right)/2;  // calculate mid point

        vector<int> b_left(m);
        vector<int> b_right(right - m);


        // get left sub array
        for (int i = 0; i < m; i++) {
            b_left[i] = a[i];
        }

        // set new endpoints
        leftt = 0;
        rightt = m;

        majority_left = get_majority_element(b_left, leftt, rightt);

        for (int i = 0; i < right - m + 1; i++) {
            b_right[i] = a[m+i];
        }

        leftt = m;
        rightt = right - m + 1;

        majority_right = get_majority_element(b_right, leftt, rightt);

        // if there is a majority element, count its frequency

        if (majority_left != -1) {
            for (int i = 0; i < a.size(); i++) {
                if (a[i] == majority_left)
                    count_left++;
            }
        }

        if (majority_right != -1) {
            for (int i = 0; i < a.size(); i++) {
                if (a[i] == majority_right)
                    count_right++;
            }
        }


        // if both elements in sub arrays are majority and they're different, there is no majority element
        if (count_left == count_right && majority_left != majority_right) {
            return -1;
        }

        // check if either sub array has a majority element that occurs more than n/2 times
        else if (count_right > count_left && count_right > a.size()/2) {
                return majority_right;
        }
        else if (count_left > count_right && count_left > a.size()/2){
            return majority_left;
        }
    }

    return -1;

}


int get_majority_fast(vector<int> &a, int left, int right){

    std::reverse(a.begin(),a.end());

    int current = 0;
    int count;
    for (int i = 0; i < a.size(); i++) {
        current = a[i];
        count = 0;
        for (int j = 0; j < a.size(); j++) {
            if (a[j] == current)
                count ++;
        }
        if (count > a.size()/2)
            return 1;
    }
    return -1;
}

int main() {
//    std::cin >> n;
//    vector<int> a(n);
//    for (size_t i = 0; i < a.size(); ++i) {
//        std::cin >> a[i];
//    }
//    std::cout << (get_majority_fast(a, 0, a.size()) != -1) << '\n';

    while(true){
        int one, two;
        int n = rand() % 100 ;
        std::cout << n << "\n";
        vector<int> a;
        for (int i = 0; i < n; ++i) {
            a.push_back(rand() % 100);
        }

        one = get_majority_element(a, 0, a.size());
        two = get_majority_fast(a,  0, a.size() != -1);

        if (one != two) {
            std::cout << "Wrong answer: " << one << ' ' << two << "\n";
            break;
        }
        else {
            std::cout << "OK\n";
        }
    }
}

person Emilio Botero    schedule 31.01.2017    source источник
comment
Почему get_majority_fast() не использует аргументы left или right? И почему вы передаете логическое значение в качестве аргумента right?   -  person Barmar    schedule 31.01.2017
comment
Запустите программу под отладчиком. Когда он остановится из-за ошибки, проверьте трассировку стека, чтобы увидеть, какой оператор получил ошибку и каковы значения переменных. Вероятно, это потому, что вы обращаетесь за пределами вектора.   -  person Barmar    schedule 31.01.2017
comment
Одним из лучших инструментов отладки при использовании std::vector является использование функции at(). Он выдаст исключение, если вы когда-нибудь выйдете за пределы вектора. Вы делаете это прямо здесь: b_right[i] = a[m + i]; -- измените это на b_right[i] = a.at(m + i), и вы должны получить исключение std::out_of_range как показано в этом примере   -  person PaulMcKenzie    schedule 31.01.2017
comment
@Barmar, я написал get_majority_fast довольно быстро, поэтому просто скопировал определение функции из предыдущего. Однако передаваемые ему аргументы не имеют значения. Я проверю, не выходит ли за пределы, спасибо.   -  person Emilio Botero    schedule 31.01.2017
comment
@PaulMcKenzie Не могли бы вы предоставить информацию, при которой это происходит? До сих пор я получал правильные результаты без каких-либо исключений за пределы (используя a[m+i])   -  person Emilio Botero    schedule 31.01.2017
comment
@EmilioBotero - вот в чем проблема. Когда вы используете [ ] для доступа к элементу вектора, это неопределенное поведение для доступа к элементу за пределами границ. Нет никакой гарантии, что программа рухнет, даже если она неправильная. Используя at() гарантии, программа останавливается при доступе за границу. Я не использовал тестовые данные - я просто использовал программу, которую вы опубликовали, и запустил ее с небольшим изменением, используя at(). Короче говоря, ваша программа всегда ошибалась, но вы никогда не знали об этом и были одурачены, увидев появляющийся вывод.   -  person PaulMcKenzie    schedule 31.01.2017
comment
@PaulMcKenzie Где-то в глубине души я знал о a.at(). Однако из Python я привык использовать [ ]. Спасибо!   -  person Emilio Botero    schedule 31.01.2017


Ответы (1)


По сути, в одном из циклов произошла ошибка за пределами границ. Исправленный цикл ниже:

for (int i = m; i < right - m ; i++) {
    b_right.at(m-i) = a.at(m + i);
}

Я думал, что странная ошибка SIGABRT возникла из-за чего-то таинственного. Думаю, я научился использовать x.at()

Кроме того, у меня было еще несколько ошибок. Полный исправленный код ниже:

#include <algorithm>
#include <iostream>
#include <vector>


using std::vector;

int get_majority_element(vector<int> &a, int left, int right) {


    int m;
    int majority_left = -1, majority_right = -1;      // majority element in either sub array
    int count_right = 0, count_left = 0;    // count for above
    int new_left, new_right;                      // endpoints

    if (a.size() == 1) {
        return a[0];
    }
    else {


        m = (a.size())/2;  // calculate mid point

        vector<int> b_left(m);
        vector<int> b_right(right - m);


        // get left sub array
        for (int i = 0; i < m; i++) {
            b_left.at(i) = a.at(i);
        }

        for (int i = 0; i < a.size() - m ; i++) {
            b_right.at(i) = a.at(i + m);
        }

        // set new endpoints
        new_left = 0;
        new_right = m;
        majority_left = get_majority_element(b_left, new_left, new_right);


        new_left = m;
        new_right = right - m;

        majority_right = get_majority_element(b_right, new_left, new_right);


        // if there is a majority element, count its frequency

        if (majority_left != -1) {
            for (int i = 0; i < a.size(); i++) {
                if (a[i] == majority_left)
                    count_left++;
            }
        }

        if (majority_right != -1) {
            for (int i = 0; i < a.size(); i++) {
                if (a[i] == majority_right)
                    count_right++;
            }
        }


        // if both elements in sub arrays are majority and they're different, there is no majority element
        if (count_left == count_right && majority_left != majority_right) {
            return 0;
        }
        else if (count_left == count_right)
            return majority_left;
        // check if either sub array has a majority element that occurs more than n/2 times
        else if (count_right > count_left && count_right > a.size()/2) {
                return majority_right;
        }
        else if (count_left > count_right && count_left > a.size()/2){
            return majority_left;
        }
            // majority element in sub arrays isn't majority in array
        else
            return 0;
    }

}


int get_majority_fast(vector<int> &a, int left, int right){

    std::reverse(a.begin(),a.end());

    int current = 0;
    int count;
    for (int i = 0; i < a.size(); i++) {
        current = a[i];
        count = 0;
        for (int j = 0; j < a.size(); j++) {
            if (a[j] == current)
                count ++;
        }
        if (count > a.size()/2)
            return 1;
    }
    return -1;
}

int main() {

    int n;
    std::cin >> n;
    vector<int> a(n);

    for (size_t i = 0; i < a.size(); ++i) {
        std::cin >> a[i];
    }

    int result;
    int out;

    result = get_majority_element(a, 0, a.size());

    if (result != 0)
        out = 1;
    else
        out = 0;
    std::cout << out << '\n';


}

Я уверен, что мог бы сделать его намного красивее, но сейчас я не хочу слышать об этом какое-то время.

person Emilio Botero    schedule 31.01.2017