4

Note: While writing this question, I think I already found the answer. Feel free to ammend or append it with a better version. I thought it might be nice to document my problem. edit I was wrong, my aswer was not correct.

Considering a list of integer pairs: I'd like to topologically sort them based on a partial ordering. This is similar to Is partial-order, in contrast to total-order, enough to build a heap? , but I'd like to use std::sort instead of std::priority_queue.

To do so I wrote this piece of code:

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


struct pair {
    int a, b;
    pair(int a, int b) : a(a), b(b) {}

    std::ostream &print(std::ostream &out) const {
        return (out << "(" << a << ", " << b << ")");
    }
};

std::ostream &operator<<(std::ostream &out, const pair &p) { return p.print(out); }

struct topological_pair_comparator {
    bool operator()(const pair &p, const pair &q) const { return p.a<q.a && p.b<q.b; }
} tpc;

std::vector<pair> pairs = {
    pair(1,1),
    pair(1,2),
    pair(2,1),
    pair(3,1),
    pair(1,3),
    pair(5,5),
    pair(2,2),
    pair(4,0)
};

int main() {
    std::sort(pairs.begin(), pairs.end(), tpc);
    for(const pair &p : pairs) std::cout << p << " ";
    std::cout << std::endl;
    return 0;
}

Source: http://ideone.com/CxOVO0

Resulting in:

(1, 1) (1, 2) (2, 1) (3, 1) (1, 3) (2, 2) (4, 0) (5, 5) 

Which is pretty much topologially sorted (proof by example ;).

However, the partial ordering creates that !((1,2) < (2,1)) and !((1,2) > (2,1)) according to the tpc, and hence one may conclude (1,2) == (2,1). However, paragraph 25.4.3 of the c++ standard (January 2012 working draft) states:

For all algorithms that take Compare, there is a version that uses operator< instead. That is, comp(*i, *j) != false defaults to *i < *j != false. For algorithms other than those described in 25.4.3 to work correctly, comp has to induce a strict weak ordering on the values.

Edited: According to ecatmur 's valid answer: A partial ordering is not necessarily a strict weak ordering; it breaks the transitivity of incomparibility. So I'd like to drop my reasoning that a partial ordering is always a strict weak ordering and the associated questions, and add the question: am I doomed to write my own topological sorting algorithm or use the boost implementation which requires me to build the graph?

Solution: A smart suggestion of ecatmur:

struct topological_pair_comparator {
    bool operator()(const pair &p, const pair &q) const { return (p.a + p.b) < (q.a + q.b); }
} tpc;

Source: http://ideone.com/uoOXNC

Please note, the SO about heaps does not explicitely mention that std::sort sorts topologically; except for one comment, which is not backed up by argumentation.

Community
  • 1
  • 1
Herbert
  • 5,279
  • 5
  • 44
  • 69
  • According to wikipedia, a partial ordering is also not a strict weak ordering: http://en.wikipedia.org/wiki/Weak_ordering#Strict_weak_orderings . However, I don't seem to get which propperty of the four mentioned is invalidated. Nor does it state the implication you mention, it just mensions that incomparability is an equivalence relation. I'm not too sure, but I can't find a counter-example of a partial ordering where incomparability is not an equivalence relation. – Herbert Jun 18 '14 at 13:14
  • where incomparability of a and b is defined as **not (a – Herbert Jun 18 '14 at 13:16
  • I derped, nevermind me. – Xeo Jun 18 '14 at 13:17
  • Your assumption is wrong. All strict weak orders are partial orders but not all partial orders are strict weak orders. A strict weak order is a partial order that has the additional property of *transitivity of incomparability*. – R. Martinho Fernandes Jun 18 '14 at 13:30
  • If you don't know *a priori* the adjacency list and you aren't fortunate enough to be able to refine the partial order to a strict weak order, then I think this question should help: http://stackoverflow.com/questions/4600258/sorting-a-poset – ecatmur Jun 18 '14 at 14:20
  • You could build a graph (e.g. using Boost.Graph) taking your input sequence as the list of vertices (let's call those `V`). Then you can do an `O(V^2)` exhaustive search on all pairs of vertices and add an edge `(x, y)` to your graph if `tpc(x, y)` is true. Then you can run a topological sort in `O(V + E)` on your graph (which is `O(V^2)` in total). – TemplateRex Nov 18 '16 at 20:22

1 Answers1

6

Consider the values

pair
    x{0, 1},
    y{2, 0},
    z{1, 2};

Here,

!tpc(x, y) && !tpc(y, x);
!tpc(y, z) && !tpc(z, y);

However,

tpc(x, z);

Thus your comparator does not impose a strict weak ordering, and behavior is undefined if you use it with std::sort or in any other role where a strict weak ordering is required.

A comparator that is strict weak and is a refinement of your comparator would be:

struct refined_comparator {
    bool operator()(const pair &p, const pair &q) const { return p.a + p.b < q.a + q.b; }
} rc;
ecatmur
  • 152,476
  • 27
  • 293
  • 366