Как отсортировать и ранжировать вектор в C++ (без использования C++11)

Я пытаюсь построить функцию, которая принимает вектор, ранжирует его, сортирует и выводит отсортированный и ранжированный вектор с исходным расположением значений. Например: Ввод: [10,332,42,0,9,0] Вывод: [3, 5, 4, 2, 1]

Я использовал этот вопрос о переполнении стека (в частности, ответ Мариуса) в качестве справочного руководства, однако сейчас я застрял в своем коде и не понимаю, в чем проблема. Я использую С++ 03.

Одна из ошибок, которые я получаю, это

error: invalid types ‘const float*[float]’ for array subscript’ for array subscript в моем заявлении if.

//Rank the values in a vector
std::vector<float> rankSort(const float *v_temp, size_t size)
{
    vector <float> v_sort;
    //create a new array with increasing values from 0 to n-1
    for(unsigned i = 0; i < size; i++)
    {
        v_sort.push_back(i);
    }
    bool swapped = false;
    do
    {
        for(unsigned i = 0; i < size; i++)
        {
            if(v_temp[v_sort[i]] > v_temp[v_sort[i+1]]) //error line
            {
                float temp = v_sort[i];
                v_sort[i] = v_sort[i+1];
                v_sort[i+1] = temp;
                swapped = true;
            }
        }
    }
    while(swapped);
    return v_sort;
}

std::vector<float> rankSort(const std::vector<float> &v_temp)
{
    return rankSort(&v_temp[0], v_temp.size());
}

person Newskooler    schedule 16.12.2016    source источник
comment
В какой строке появляется ошибка? Пожалуйста, укажите это, например. комментарий.   -  person Some programmer dude    schedule 16.12.2016
comment
Кроме того, почему вы передаете указатель на функцию сортировки? Почему бы не отсортировать его в функции, принимающей вектор?   -  person Some programmer dude    schedule 16.12.2016
comment
@Someprogrammerdude готово   -  person Newskooler    schedule 16.12.2016
comment
Наконец, во внутреннем цикле сортировки у вас есть ошибка «один за другим». Когда i == size - 1 какой элемент в v_sort будет проиндексирован i + 1?   -  person Some programmer dude    schedule 16.12.2016
comment
@Someprogrammerdude, так я переполняю некоторые из своих других функций, поэтому здесь я применил тот же метод. Я понимаю, что для данного конкретного примера это не нужно.   -  person Newskooler    schedule 16.12.2016
comment
Какой компилятор вы используете? Почему С++03? Скорее всего есть более свежая версия, которую можно использовать. Например, Visual Studio впервые предложила функции C++11 в версии 2010 года и C++14 в 2012 году. Сам компилятор можно загрузить бесплатно.   -  person Panagiotis Kanavos    schedule 16.12.2016


Ответы (5)


Ваша проблема заключается в неправильном представлении о ранжировании. Индексы массива имеют значение size_t, а не float, поэтому вам нужно будет вернуть vector<size_t>, а не vector<float>.

Тем не менее, ваша сортировка O(n2). Если вы хотите использовать больше памяти, мы можем сократить это время до O(n log(n)):

vector<size_t> rankSort(const float* v_temp, const size_t size) {
    vector<pair<float, size_t> > v_sort(size);

    for (size_t i = 0U; i < size; ++i) {
        v_sort[i] = make_pair(v_temp[i], i);
    }

    sort(v_sort.begin(), v_sort.end());

    pair<double, size_t> rank;
    vector<size_t> result(size);

    for (size_t i = 0U; i < size; ++i) {
        if (v_sort[i].first != rank.first) {
            rank = make_pair(v_sort[i].first, i);
        }
        result[v_sort[i].second] = rank.second;
    }
    return result;
}

Живой пример

ИЗМЕНИТЬ:

Да, это на самом деле становится немного проще, если взять vector<float> вместо float[]:

vector<size_t> rankSort(const vector<float>& v_temp) {
    vector<pair<float, size_t> > v_sort(v_temp.size());

    for (size_t i = 0U; i < v_sort.size(); ++i) {
        v_sort[i] = make_pair(v_temp[i], i);
    }

    sort(v_sort.begin(), v_sort.end());

    pair<double, size_t> rank;
    vector<size_t> result(v_temp.size());

    for (size_t i = 0U; i < v_sort.size(); ++i) {
        if (v_sort[i].first != rank.first) {
            rank = make_pair(v_sort[i].first, i);
        }
        result[v_sort[i].second] = rank.second;
    }
    return result;
}

Живой пример

person Jonathan Mee    schedule 16.12.2016
comment
Спасибо за ответ. Однако я попытался ввести вектор, а не массив (хотя, скорее всего, это не производит такого впечатления в моем коде). Я проверил ваш код, и он отлично работает для массивов. Не могли бы вы сообщить мне, если / как это может работать для векторного ввода? - person Newskooler; 16.12.2016
comment
@Newskooler Круто, я в любом случае добавил код. Если вы новичок в vectors, вы захотите взять их по константной ссылке, как в моем примере. - person Jonathan Mee; 16.12.2016
comment
В целом я новичок в С++, поэтому сейчас читаю оба ваших примера. Спасибо! - person Newskooler; 16.12.2016
comment
@Newskooler Итак, у меня работает ваш код: ideone.com/IP9ALW, но я понял, что он выводит что-то более простое, чем мой выдает. Ваш печатает сопоставление отсортированного массива с v_temp. Мой печатает рейтинг каждого элемента в массиве. Если вы хотите, чтобы отображение было немного проще, вы можете сделать это следующим образом: ideone.com/abP5VQ что вы искали? Должен ли я вместо этого поместить код сопоставления в свой ответ? - person Jonathan Mee; 16.12.2016

v_sort[i] - это float (это просто элемент вектора v_sort), а в качестве индексов массива могут использоваться только целочисленные типы.

Вероятно, вы имели в виду v_sort как массив индексов, поэтому вы должны объявить его как std::vector<size_t> или std::vector<int> что-то в этом роде.

UP: Кроме того, учитывая, что вы меняете значения переданного массива, это не элегантный способ передать его по ссылке const.

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

std::vector<unsigned> rankSort(float *v_temp, size_t size)
{
    vector <unsigned> v_sort;
    //create a new array with increasing values from 0 to n-1
    for(unsigned i = 0; i < size; i++)
    {
        v_sort.push_back(i);
    }
    bool swapped = false;
    do
    {
        for(unsigned i = 0; i < size; i++)
        {
            if(v_temp[v_sort[i]] > v_temp[v_sort[i+1]]) //error line
            {
                unsigned temp = v_sort[i];
                v_sort[i] = v_sort[i+1];
                v_sort[i+1] = temp;
                swapped = true;
            }
        }
    }
    while(swapped);
    return v_sort;
}

std::vector<unsigned> rankSort(std::vector<float> &v_temp)
{
    return rankSort(&v_temp[0], v_temp.size());
}
person alexeykuzmin0    schedule 16.12.2016
comment
Оригинальный пример с массивом, поэтому я пытаюсь интегрировать его для векторов. - person Newskooler; 16.12.2016
comment
В любом случае, если вы планируете хранить индексы в v_sort, это должен быть контейнер целочисленных значений. Индекс вектора или массива 0,5 не имеет смысла. - person alexeykuzmin0; 16.12.2016
comment
хорошо, поэтому я могу изменить тип v_sort на int вместо float, однако ошибка все еще сохраняется. - person Newskooler; 16.12.2016
comment
@Newskooler И тип возвращаемого значения обеих функций, поскольку вы просто возвращаете v_sort - person alexeykuzmin0; 16.12.2016
comment
Вы правы: последний бит, когда я вызываю функцию, вводя только значение vector <float>, я получаю следующую ошибку: error: no matching function for call to ‘rankSort(<unresolved overloaded function type>)’ - person Newskooler; 16.12.2016
comment
Если происходит обмен, он должен зацикливаться навсегда. Кроме того, ваш основной цикл сравнивает последний элемент с v_temp[v_sort[size]]... Я не знаю, как ваш код работает для несортированных векторов. - person Alexander Anikin; 16.12.2016
comment
@AlexanderAnikin Вы правы, я имел в виду, что компилируется правильно. Обновлен ответ - person alexeykuzmin0; 16.12.2016
comment
@alexeykuzmin0 Я протестировал код, и у меня он продолжает зацикливаться. Если я ввожу следующий вектор: [30,0,3,302,-50], он просто зацикливается. - person Newskooler; 16.12.2016
comment
Да, @AlexanderAnikin уже исправил некоторые проблемы. Если зацикливание его кода продолжается, рассмотрите возможность использования инструмента отладки, он действительно хорош для решения проблем такого рода. - person alexeykuzmin0; 16.12.2016

Я предлагаю вам принять более надежное решение, воспользовавшись тем, что у вас есть в STL. Для этого мы сначала создадим «индексный вектор», т.е. std::vector<std::size_t> v такое, что для любого i верно v[i] == i:

// I'm sure there's a more elegant solution to generate this vector
// But this will do
std::vector<std::size_t> make_index_vector(std::size_t n) {
    std::vector<std::size_t> result(n, 0);
    for (std::size_t i = 0; i < n; ++i) {
        result[i] = i;
    }
    return result;
}

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

template <typename T, typename A, typename Cmp>
struct idx_compare {
    std::vector<T, A> const& v;
    Cmp& cmp;
    idx_compare(std::vector<T, A> const& vec, Cmp& comp) : v(vec), cmp(comp) {}

    bool operator()(std::size_t i, std::size_t j) {
        return cmp(v[i], v[j]);
    }
};

template <typename T, typename A, typename Cmp>
std::vector<std::size_t> sorted_index_vector(std::vector<T, A> const& vec, Cmp comp) {
    std::vector<std::size_t> index = make_index_vector(vec.size());
    std::sort(index.begin(), index.end(),
        idx_compare<T, A, Cmp>(vec, comp));

    return index;
}

В отсортированном индексном векторе index[0] — это индекс наименьшего значения во входном векторе, index[1] — второе наименьшее значение и так далее. Следовательно, нам нужен еще один шаг, чтобы получить вектор рангов из этого:

std::vector<std::size_t> get_rank_vector(std::vector<std::size_t> const& index) {
    std::vector<std::size_t> rank(index.size());
    for (std::size_t i = 0; i < index.size(); ++i) {
        // We add 1 since you want your rank to start at 1 instead of 0
        // Just remove it if you want 0-based ranks
        rank[index[i]] = i + 1;
    }
    return rank;
}

Теперь соединяем все части вместе:

template <typename T, typename A, typename Cmp>
std::vector<std::size_t> make_rank_vector(
    std::vector<T, A> const& vec, Cmp comp) {
    return get_rank_vector(sorted_index_vector(vec, comp));
}

// I had to stop using default template parameters since early gcc version did not support it (4.3.6)
// So I simply made another overload to handle the basic usage.
template <typename T, typename A>
std::vector<std::size_t> make_rank_vector(
    std::vector<T, A> const& vec) {
    return make_rank_vector(vec, std::less<T>());
}

Результат с [10, 332, 42, 0,9, 0]: [3, 5, 4, 2, 1]. Вы можете найти демонстрацию на gcc 4.3.6, чтобы это поведение.

person Rerito    schedule 16.12.2016
comment
Ммм... Он сказал, что уже знает, как это сделать на С++ 11, он пытался найти решение на С++ 03. Что это не так. - person Jonathan Mee; 16.12.2016
comment
@JonathanMee, тогда просто замените лямбду на собственный функтор и удалите autos. - person Rerito; 16.12.2016
comment
Если это так просто, почему бы не исправить свой код, чтобы он отвечал на вопрос? Лично я не думаю, что это так просто, потому что у вас есть захватывающая лямбда. - person Jonathan Mee; 16.12.2016
comment
@JonathanMee Только что сделал - person Rerito; 16.12.2016
comment
Итак, еще один вопрос, почему вы используете template <typename T, typename A> Зачем передавать распределитель? Я не вижу, чтобы ты что-то с этим делал. - person Jonathan Mee; 16.12.2016
comment
@JonathanMee Я просто хочу, чтобы это было как можно более общим. Функции будут работать, если пользователь введет в них вектор с помощью настраиваемых аллокаторов. - person Rerito; 16.12.2016
comment
Хорошее решение. Вероятно, вы могли бы значительно упростить это, но это делает то, что я сделал бы с лямбдой, если бы у меня был С++ 11, что мне нравится. - person Jonathan Mee; 16.12.2016

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

template <typename T>
vector<size_t> calRank(const vector<T> & var) {
    vector<size_t> result(var.size(),0);
    //sorted index
    vector<size_t> indx(var.size());
    iota(indx.begin(),indx.end(),0);
    sort(indx.begin(),indx.end(),[&var](int i1, int i2){return var[i1]<var[i2];});
    //return ranking
    for(size_t iter=0;iter<var.size();++iter){
        result[indx[iter]]=iter+1;
    }
    return result;
}
person drbombe    schedule 03.12.2017

person    schedule
comment
Я проверил ваш код, и для вектора ввода [30,0,3,302,-50] я получаю следующий вывод [4,1,2,0,3], но я должен получать [3,1,2,4,0] - person Newskooler; 16.12.2016
comment
Wnaser [4,1,2,0,3] соответствует [-50, 0, 3, 30, 302], желаемый результат соответствует [302, 0, 3, -50, 30]. Вы уверены, что должны получить [3,1,2,4,0]? - person Alexander Anikin; 16.12.2016
comment
Да, я проверял это несколько раз, и я пытаюсь выяснить, почему это происходит. Если это поможет, вот мой код для входного вектора: vector <float> testing; testing.push_back(30); testing.push_back(0); testing.push_back(3); testing.push_back(302); testing.push_back(-50); вызов функции vector<size_t> ranked = rankSort(testing); - person Newskooler; 16.12.2016
comment
@Newskooler - пример Александра Аникина создает вектор индексов, отсортированных по v_temp. Чтобы преобразовать в ранг, создайте вектор v_rank и используйте for(i = 0; i ‹ v_temp.size; i++) / v_rank[v_sort[i]] = i; . Это преобразует v_sort из [4,1,2,0,3] в v_rank[3,1,2,4,0]. - person rcgldr; 16.12.2016