2

I am trying to construct a function take takes a vector, ranks it, sorts it and outputs the sorted and ranked vector with the original positioning of the values. For example: Input: [10,332,42,0.9,0] Output: [3, 5, 4, 2, 1]

I used this stack overflow question (specifically Marius' answer) as a reference guide, however I am stuck with my code now and do not understand where the issue is. I am running a C++03.

One of the errors I get is

error: invalid types ‘const float*[float]’ for array subscript’ for array subscript on my if statement.

//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());
}
Community
  • 1
  • 1
Newskooler
  • 3,973
  • 7
  • 46
  • 84

5 Answers5

1

v_sort[i] is a float (it's just an element of v_sort vector) while only integral types can be used as array subscripts.

Probably you meant v_sort as an array of indices, thus, you should declare it as std::vector<size_t> or std::vector<int> something like that.

UP: Also, given that you change the values of the array passed, it's not an elegant way of pass it by const reference.

To sum up, the following code compiles correctly on my machine:

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());
}
alexeykuzmin0
  • 6,344
  • 2
  • 28
  • 51
  • The original example is with an array, so I am trying to integrate it for vectors. – Newskooler Dec 16 '16 at 12:41
  • Anyway, if you plan to store indices in `v_sort`, it should be a container of integer values. Vector or array index 0.5 doesn't make sense. – alexeykuzmin0 Dec 16 '16 at 12:44
  • okay, so I can change the type of `v_sort` to `int` instead of `float`, however the error still persists. – Newskooler Dec 16 '16 at 12:45
  • @Newskooler And the return type of both functions also, as you just return `v_sort` – alexeykuzmin0 Dec 16 '16 at 12:46
  • You are correct: One final bit, when I call the function, by inputting only a `vector ` value, I get the following error: `error: no matching function for call to ‘rankSort()’` – Newskooler Dec 16 '16 at 12:49
  • 1
    If swap happens, it should loop forever. Also, your main loop compares last element to v_temp[v_sort[size]]... I don't know how your code works for non-sorted vectors. – Alexander Anikin Dec 16 '16 at 13:02
  • @AlexanderAnikin You right, I meant compiles correctly. Updated the answer – alexeykuzmin0 Dec 16 '16 at 13:05
  • @alexeykuzmin0 I tested the code and for me it keeps looping. If I input the following vector: `[30,0,3,302,-50]` it just goes in a forever loop. – Newskooler Dec 16 '16 at 13:23
  • Yes, @AlexanderAnikin has already fixed some issues. If looping of his code continues, consider using debugger tool, it's really good in dealing with this sort of problems – alexeykuzmin0 Dec 16 '16 at 13:25
1
//Rank the values in a vector
std::vector<size_t> rankSort(const std::vector<float> &v_temp)
{
    vector <size_t> v_sort;
    //create a new array with increasing values from 0 to size-1
    for(size_t i = 0; i < v_temp.size(); i++)
        v_sort.push_back(i);

    bool swapped = false;
    do
    {
        swapped = false; //it's important to reset swapped
        for(size_t i = 0; i < v_temp.size()-1; i++) // size-2 should be the last, since it is compared to next element (size-1)
            if(v_temp[v_sort[i]] > v_temp[v_sort[i+1]]) 
            {
                size_t temp = v_sort[i]; // we swap indexing array elements, not original array elements
                v_sort[i] = v_sort[i+1];
                v_sort[i+1] = temp;
                swapped = true;
            }
    }
    while(swapped);

    return v_sort;
}
Alexander Anikin
  • 1,108
  • 6
  • 14
  • I tested your code and for a vector of input `[30,0,3,302,-50]` I get the following output `[4,1,2,0,3]`, but I should be getting `[3,1,2,4,0]` – Newskooler Dec 16 '16 at 13:20
  • Wnaser [4,1,2,0,3] corresponds to [-50, 0, 3, 30, 302], your desired output corresponds to [302, 0, 3, -50, 30]. Are you sure you should get [3,1,2,4,0]? – Alexander Anikin Dec 16 '16 at 13:29
  • Yes, I tested it a few times and I am trying to find out why this happens. If it helps, here is my code for the input vector: `vector testing; testing.push_back(30); testing.push_back(0); testing.push_back(3); testing.push_back(302); testing.push_back(-50);` calling the function `vector ranked = rankSort(testing);` – Newskooler Dec 16 '16 at 13:34
  • 1
    @Newskooler - Alexander Anikin's example produces a vector of indices sorted according to v_temp. To convert to a rank, create a vector v_rank, and use for(i = 0; i < v_temp.size; i++) / v_rank[v_sort[i]] = i; . This will convert v_sort of [4,1,2,0,3] to v_rank[3,1,2 ,4,0]. – rcgldr Dec 16 '16 at 14:11
1

Your problem is a misconception on rankings. Array indices are of size_t not float, so you'll need to return a vector<size_t> not a vector<float>.

That said your sort is O(n2). If you're willing to use more memory we can get that time down to 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;
}

Live Example

EDIT:

Yeah this actually gets a little simpler when taking a vector<float> instead of a 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;
}

Live Example

Jonathan Mee
  • 37,899
  • 23
  • 129
  • 288
  • Thanks for the answer. I tried to input a vector however and not an array (albeit it's most likely not giving this impression in my code). I tested your code and it works perfectly for arrays. Would you let me know if / how it can work for a vector input? – Newskooler Dec 16 '16 at 13:48
  • @Newskooler Cool, I've added code either way. If you're new to `vector`s you'll want to take them by const reference, as in my example. – Jonathan Mee Dec 16 '16 at 13:57
  • I'm quite new to C++ in general, so I am reading through both your examples now. Thanks! – Newskooler Dec 16 '16 at 14:03
  • @Newskooler So, I got your code working: http://ideone.com/IP9ALW but I realized that it outputs something simpler than mine is outputting. Yours is printing the mapping of the sorted array to `v_temp`. Mine is printing the ranking of each element in the array. If you want the mapping that's a little simpler, you can get that like this: http://ideone.com/abP5VQ Is that what you were looking for? Should I put the mapping code in my answer instead? – Jonathan Mee Dec 16 '16 at 14:17
1

I suggest you adopt a more robust solution by taking advantage of what you have in the STL. To do so, we will first make an "index vector", ie. a std::vector<std::size_t> vsuch that for any i, v[i] == i is true:

// 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;
}

Now all we have to do is to sort this vector according to a specific comparison function that will use the input vector. Furthermore, to allow for the most generic approach we will give the user the opportunity to use any comparison functor:

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;
}

In the sorted index vector, index[0] is the index of the lowest value in the input vector, index[1] the second lowest and so on. Therefore, we need one additional step to get the rank vector from this one:

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;
}

Now we combine all the pieces together:

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>());
}

Result with [10, 332, 42, 0.9, 0]: [3, 5, 4, 2, 1]. You can find a Live Demo on gcc 4.3.6 to explicit this behavior.

Rerito
  • 5,886
  • 21
  • 47
  • Ummm... He said he already knew how to do it in C++11 he was trying to find a C++03 solution. Which this is not. – Jonathan Mee Dec 16 '16 at 14:19
  • @JonathanMee then just replacing the lambda with a custom functor and removing the `auto`s will do – Rerito Dec 16 '16 at 14:38
  • If it's that simple why not correct your code so it answers the question? Personally I don't think it's that simple because you have a capturing lambda. – Jonathan Mee Dec 16 '16 at 14:47
  • 1
    @JonathanMee Just did – Rerito Dec 16 '16 at 14:47
  • So another question here, why are you using `template ` Why pass the allocator? I don't see you doing anything with it. – Jonathan Mee Dec 16 '16 at 15:33
  • @JonathanMee It is just to keep it the most generic I can. The functions will work if the user feeds it a vector using custom allocators – Rerito Dec 16 '16 at 15:38
  • Nice solution. You could probably streamline that a lot, but it does what I would have done with a lambda if I'd had C++11, which I like. – Jonathan Mee Dec 16 '16 at 15:51
1

Here is my codes using STL to achieve this in a concise way to get the rank.

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;
}
drbombe
  • 609
  • 7
  • 15