tags:

views:

112

answers:

4

I need to sort some arrays of floats, modify the values, and then construct an array with the original ordering, but the modified values.

In R, I could use the rank() and order() functions to achieve this:
v a vector
v[order(v)] is sorted
v[i] goes in the rank(v)th spot in the sorted vector

Is there some equivalent of these functions in the standard c or c++ libraries? A permutation matrix or other way of encoding the same information would be fine too.
O(n) space and O(nlogn) time would be ideal.

A: 

http://www.cplusplus.com/reference/algorithm/sort/

you can stuff your float into a struct that also holds an int, build an array of these structs and store the position of each element in the int field. use the stl sort with a compare function that operates on the float field for your first step, perform your calculations, then sort with a compare function that operates on the int field to get back your original order.

there might be a better way to do this, i'm not really a c++ guy

paintcan
and a brief addendum, you could skip the clumsiness of dealing with a struct and create an array of pointers to your original array of floats. write a comparison function that sorts based on the value the float * points at, do your calculations using the pointer array, and you will never have lost your original order
paintcan
Thanks, I had originally considered a similar idea, but wanted to avoid sorting twice. Storing the array of pointers seems like it would work best for what I want.
Larry Wang
+2  A: 

There is the equivalent to the rank function in C++: it's called nth_element and can be applied on any model of Random Access Container (among which vector and deque are prominent).

Now, the issue seems, to me, that the operate on values might actually modify the values and thus the ranks would change. Therefore I would advise storing the ranks.

  1. std::vector<float> to std::vector< std::pair<float, rank_t> >
  2. Sort the vector (works without any predicate)
  3. Operate on the values
  4. std::vector< std::pair<float, rank_t> > to std::vector<float>

Unless of course you want nth_element to be affected by the current modifications of the values that occurred.

Matthieu M.
Thanks, this is exactly what I was looking for. I think storing an array of pointers before sorting might be better performance-wise though.
Larry Wang
Hum.. yes it could. `std::vector<float>` to `std::vector<float*>`, sort the pointer version and operate on it.
Matthieu M.
For a simple data type, I don't see how using pointer indirection will help performance. You are going to be taking two memory hits per access to compare where your swap operation will still be a simple 4 byte swap on a 32bit machine. Sorting via pointers only helps if the cost of the swap the original data type is larger than the cost of swapping pointers.
Torlack
But on the other hand you don't have to generate a ranked vector. Honestly I think it's worth checking rather than arguing over it. And with luck (if it's small enough), none will seem faster anyway.
Matthieu M.
Yes, I wanted to avoid the overhead of using vectors, since none of their other features are useful here, and I half expected to have to use doubles instead (and have since made that change).
Larry Wang
A: 

The simple answer is to copy the arrays before sorting them, and restore them when you've finished. Only do something more complicated if speed is an issue.

In C++, you could sort an array of indices using a custom comparator to compare the array values, something vaguely like this (not tested, and assuming the floats are in a vector):

class Comparator
{
public:
    explicit Comparator(const std::vector<float>& array) : array(array) {}
    bool operator()(size_t a, size_t b) {return array[a] < array[b];}
private:
    const std::vector<float>& array;
};

void Example(const std::vector<float> &floats)
{
    std::vector<size_t> rank;
    for (int i = 0; i < floats.size(); ++i) rank.push_back(i);
    std::sort(rank.begin(), rank.end(), Comparator(floats));

    std::cout << "First: " << floats[rank.front()] << std::endl;
    std::cout << "Last:  " << floats[rank.back()]  << std::endl;
}

Or, at the cost of extra memory, you could build a vector of (float, rank) pairs, and sort that based on the float values. This approach would also work in C using qsort().

Mike Seymour
+1  A: 

The answer is not to sort the floats, but instead make an array of pointers to the floats, and sort the array of pointers using a comparison function that dereferences the pointers and compares the floats they point to.

R..