简体   繁体   中英

Why is this std::sort comparison failing?

I have a vector of vectors of unsigned ints. Each element of the parent vector is a vector of three unsigned ints. I primarily want to sort the parent vector in descending order of the first element of the child vectors, but I also want to order any child vectors that have the same first element in ascending order of the third element. I initially did this with the following code:

sort(league_vector.begin(), league_vector.end());
reverse(league_vector.begin(), league_vector.end());
sort(league_vector.begin(), league_vector.end(),
   [](const std::vector<unsigned int>& a, const std::vector<unsigned int>& b) {return a[0] == b[0] && a[2] < b[2];});

So just sorting, then reversing, the whole thing, which will order by the first element. Then a custom sort using a lambda function which should only return true if the third element is smaller and the first element is equal.

This seems to work fine when I have a relatively small number of elements in the parent vector (around 50 or less), but when I have more than this the final ordering is coming out pretty jumbled with no obvious pattern at all.

I've replaced this with a single custom sort:

sort(league_vector.begin(), league_vector.end(),
   [](const std::vector<unsigned int>& a, const std::vector<unsigned int>& b)
   {return ((a[0] > b[0]) || (a[0] == b[0] && a[2] < b[2]));});

So this returns true either when the first element is larger, or when the third element is smaller AND the first element is the same. This seems to work fine so I am just using that, but I can't work out what's wrong with the first approach. Particularly as the first approach seems to work some of the time, and the second comparison is just an extension of the first anyway.

First of all, std::sort is not a stable sort, which means it doesn't preserve the order of equivalent elements. If you want a stable sort, use std::stable_sort . Also, your custom comparison function makes no sense. Let's analyze how it behaves:

If a[0] is equal to b[0] , your function returns the result of comparing a[2] and b[2] . However, if a[0] is not equal to b[0] , your function always returns false. Since equivalence is defined as !(a < b) && !(b < a) , according to your comparison function, any two vectors with different first elements are equal.

This function also isn't a valid comparison function because it does not satisfy a strict weak ordering. Any two vectors with different first elements are equal but two vectors with the same first element are not necessarily equal. This means that if a = {1, 2, 3} , b = {2, 3, 4} , and c = {1, 3, 4} , a == b and b == c but a != c .

Why did your first attempt fail? Let's take a concrete example and focus on this comparison, and try to explain why this is invalid by doing a simple test.

Here are the two league vectors as an example:

std::vector<std::vector<unsigned int>> league_vector = {{1,2,3,4}, {2,3,4,5}, {4,5,6,7}};

Now give this to std::sort :

std::sort(league_vector.begin(), league_vector.end(),
          [](const std::vector<unsigned int>& a, 
          const std::vector<unsigned int>& b) {return a[0] == b[0] && a[2] < b[2];});

Concentrate on this:

a[0] == b[0]

So let's say std::sort gives your comparison the first two vectors in league_vector in this order

a={1,2,3,4} b={2,3,4,5}

Your comparison function will return false since a[0] != b[0] .

Then what if the compiler does a switch up, and gives you this right afterwards, just to see if your function is not ambiguous:

a={2,3,4,5} b={1,2,3,4}

In other words, a simple switch of the values. You again return false since a[0] != b[0] .

How could that make sense, where you are saying on the first test a should come after b and at the second test with just switched values, a should come after b ?

The sort algorithm justifiably becomes confused, and puts values in some unorthodox order.

Note that the Visual Studio compiler does this test I described, where the comparison function is given a and b , the return value is checked, and then b and a and the return value checked. If there is an inconsistency as pointed out, the debug runtime asserts with an "invalid comparison" (or similar) condition.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM