Definition:
Let < be a binary relation where a < b means "a is less than b".
Let > be a binary relation where a > b means "a is greater than b".
So, we assume < and > have meanings we usually use in a daily life. Though, in some programming languages (e.g. C++), we can overload them to give them different definitions, hereafter we don't think about that.
Context:
As far I read mathematical definition of strict weak ordering (e.g. Wikipedia), I think both < and > satify it. However, all examples I saw in many websites refer only to <. There is even a website which says
what they roughly mean is that a Strict Weak Ordering has to behave the way that "less than" behaves: if a is less than b then b is not less than a, if a is less than b and b is less than c then a is less than c, and so on.
Also, in N4140 (C++14 International Standard), strict weak ordering is defines as
(§25.4-4) If we define equiv(a, b) as !comp(a, b) && !comp(b, a), then the requirements are that comp and equiv both be transitive relations
where comp is defined as
(§25.4-2) Compare is a function object type (20.9). The return value of the function call operation applied to an object of type Compare, when contextually converted to bool (Clause 4), yields true if the first argument of the call is less than the second, and false otherwise. Compare comp is used throughout for algorithms
assuming an ordering relation.
Question:
Does ">" satisfy strict weak ordering? I expect so, but have no confidence.
Does greater operator “>” satisfy strict weak ordering?
The mathematical strict greater than relation is a strict weak ordering.
As for the operator in C++ langauge: For all integers types: Yes. In general: No, but in most cases yes. Same applies to strict less than operator.
As for the confusing quote, "is less than" in that context intends to convey that means that the the end result of the sort operation is a non-decreasing sequence i.e. objects are "less" or equal to objects after them. If std::greater is used as comparison object, then greater values are "lesser" in order.
This may be confusing, but is not intended to exclude strict greater than operator.
what is the case where > doesn't satisfy strict weak ordering?
Some examples:
Overloaded operators that don't satisfy the properties.
> operator on pointers that do not point to the same array has unspecified result.
> does not satisfy irreflexivity requirement for floating point types in IEEE-754 representation unless NaNs are excluded from the domain.
Even if the standard refers to "less than" for arbitrary Compare functions, that only implies "less than" in the context of the ordering.
If I define an ordering by comparison function [](int a, int b) { return a > b; }, then an element is "less than" another in this ordering if its integer value is greater. That's because the ordering I've created is an ordering of the integers in reverse order. You shouldn't read < as "less than" in orderings. You should read it as "comes before".
Whenever x < y is a strict weak ordering then x > y is also a strict weak ordering, just with the reverse order.
Related
In Stroustrup C++ 4th Ed Page 891, where comparisons properties are described. He explains that the function cmp can be represented by less than < for a strict weak ordering. I'm confused by his explanation of "Transitivity of equivalence" as follows;
Transitivity of equivalence: Define equiv(x,y) to be
!(cmp(x,y)||cmp(y,x)). If equiv(x,y) and equiv(y,z), then equiv(x,z).
The last rule is the one that allows us to define equality (x==y) as !(cmp(x,y)||cmp(y,x)) if we need ==.
Should this instead be defined as follows?
cmp is <= and equiv(x,y) = (cmp(x,y) && cmp(y,x))
Appreciate your guidance.
This is not errata.
equiv(x,y) := !(cmp(x,y)||cmp(y,x))
x := x
y := x
substituting in:
!((x < x) || (x < x))
!((false) || (false))
!(false)
true
Can this instead be defined as follows?
cmp is <= and equiv(x,y) = (cmp(x,y) && cmp(y,x))
Yes, that also gives you consistent definitions.
Should this instead be defined as follows?
It isn't better than the definitions we use, so I'd suggest no, mostly because there's loads of existing code written for the current definition.
Instead of the current definition of Compare
Compare is a set of requirements expected by some of the standard library facilities from the user-provided function object types.
The return value of the function call operation applied to an object of a type satisfying Compare, when contextually converted to bool, yields true if the first argument of the call appears before the second in the strict weak ordering relation induced by this type, and false otherwise.
For all a, cmp(a,a)==false
If cmp(a,b)==true then cmp(b,a)==false
If cmp(a,b)==true and cmp(b,c)==true then cmp(a,c)==true
It would instead be
The return value of the function call operation applied to an object of a type satisfying Compare, when contextually converted to bool, yields false if the first argument of the call appears after the second in the strict weak ordering relation induced by this type, and true otherwise.
For all a, cmp(a,a)==true
If cmp(a,b)==false then cmp(b,a)==true
If cmp(a,b)==true and cmp(b,c)==true then cmp(a,c)==true
Why does STL work with a comparison function that is strict weak ordering? Why can't it be partial ordering?
A partial order would not be sufficient to implement some algorithms, such as a sorting algorithm. Since a partially ordered set does not necessarily define a relationship between all elements of the set, how would you sort a list of two items that do not have an order relationship within the partial order?
Simply, a strict weak ordering is defined as an ordering that defines a (computable) equivalence relation. The equivalence classes are ordered by the strict weak ordering: a strict weak ordering is a strict ordering on equivalence classes.
A partial ordering (that is not a strict weak ordering) does not define an equivalence relation, so any specification using the concept of "equivalent elements" is meaningless with a partial ordering that is not a strict weak ordering. All STL associative containers use this concept at some point, so all these specifications are meaningless with a partial ordering that is not a strict weak ordering.
Because a partial ordering (that is not a strict weak ordering) does not necessarily define any strict ordering, you cannot "sort elements" in the common sense according to partial ordering (all you can do is a "topological sort" which has weaker properties).
Given
a mathematical set S
a partial ordering < over S
a value x in S
you can define a partition of S (every element of S is either in L(x), I(x) or G(x)):
L(x) = { y in S | y<x }
I(x) = { y in S | not(y<x) and not(x<y) }
G(x) = { y in S | x<y }
L(x) : set of elements less than x
I(x) : set of elements incomparable with x
G(x) : set of elements greater than x
A sequence is sorted according to < iff for every x in the sequence, elements of L(x) appear first in the sequence, followed by elements of I(x), followed by elements of G(x).
A sequence is topologically sorted iff for every element y that appears after another element x in the sequence, y is not less than x. It is a weaker constraint than being sorted.
It is trivial to prove that every element of L(x) is less than any element of G(x). There is no general relation between elements of L(x) and elements of I(x), or between elements of I(x) and elements of G(x). However, if < is a strict weak ordering, than every element of L(x) is less than any element of I(x), and than any element of I(x) is less than any element of G(x).
If < is a strict weak ordering, and x<y then any element of L(x) U I(x) is less then any element I(y) U G(y): any element not greater than x is less than any element not less that y. This does not necessarily hold for a partial ordering.
Quoting the answer given here:
Because internally, these algorithms implement "is equal to" as !(a < b) && !(b < a).
If you used <= to implement the less-than operator, then the above
would return false when a == b. A broken equality check will screw up
nearly any algorithm.
Similarly, they implement "is not equal to" as (a < b) || (b < a)
, and once again, if you implemented the < operator using <=, then it
will return true when they are equal to each other, when in fact they
are not equal. So the equality check is broken in both directions.
The whole point of limiting the library to a less-than operator is
that all of the logical operators can be implemented in terms of it:
<(a, b): (a < b)
<=(a, b): !(b < a)
==(a, b): !(a < b) && !(b < a)
!=(a, b): (a < b) || (b < a)
>(a, b): (b < a)
>=(a, b): !(a < b)
This works as long as your provided operator meets the conditions of a
strict weak ordering. The standard <= and >= operators do not.
You cannot perform binary search with partial ordering. You cannot create a binary search tree with partial ordering. What functions/datatypes from algorithm need ordering and can work with partial ordering?
I have found multiple times, while reading some concepts definitions, the use of the term equal, like in Swappable:
Let t1 and t2 be equality-preserving expressions that denote distinct equal objects of type T,
Is equal defined somewhere in the standard? I guess it means that the semantics of two objects, or the value they refer (the human semantics given to their represented domain value) are the same, even if the objects are not comparable (no operator== overloaded), or something abstract like that (like, two objects a and b are equal if a == b would yield true assuming it is a valid expression --for example, because operator== is not defined because it's not required).
Since templates are going to work on the semantics the user gives, the concept of equality is largely defined by the program. For example, if I am working with case insensitive strings, I can consider the strings FoO and fOo to be equal and FoO and bAr to be unequal. The operations I supply must reflect this semantics.
Equality isn't defined based on operator==; on the contrary, operator== is (in a sense) defined based on equality. [concept.equalitycomparable]/equality_comparable:
template<class T>
concept equality_comparable = weakly-equality-comparable-with<T, T>;
Let a and b be objects of type T. T models
equality_comparable only if bool(a == b) is true when a is
equal to b ([concepts.equality]), and false otherwise.
[ Note: The requirement that the expression a == b is
equality-preserving implies that == is transitive and symmetric.
— end note ]
I asked me how the cmp function in std::sort and std::is_sorted is defined.
here are two documentations for is_sorted_until how say it should be operator< :
en.cppreference.com
cplusplus.com
But i think there should be a problem with equal elements.
The list {1,1,1} should not be sorted because 1<1==false.
But there is an example which says:
...
int *sorted_end = std::is_sorted_until(nums, nums + N);
...
1 1 4 9 5 3 : 4 initial sorted elements
but that should return 1 if < is used like documented.
It would work with <=, but that is not the way it is documented.
I'm really confused.
The comparison is required to define a strict weak ordering. A strict weak ordering defines a set of equivalence classes from the incomparability relation, i.e., if x < y is false, and y < x is false too (i.e. x and y cannot be compared with <), x and y are considered equivalent. These equivalence classes have a total order, and that's the total order resulting from the sort functions.
In the example given, {1,1,1} has only a single equivalence class, the one composed of {1,1,1}.
is_sorted_until finds the first element x[i] for which x[i] < x[i-1] is true.
To be exact, it's neither < nor <=, it is defaulted to std::less. That one in turn calls < for most types, except where it is specialized. For example, < for pointers does not generally give a strict ordering, while std::less does.
It does indeed use operator< unless you provide a custom comparison. But the definition of "sorted" is not a[n] < a[n+1] (which we might call "strictly sorted"), but !(a[n+1] < a[n]); so equal elements are considered sorted. This is equivalent to using <=, but (in common with all other standard algorithms) doesn't require that operator to be defined.
In general, all ordered comparisons must define a "strict weak ordering". "Strict" means that the comparison must be false for equivalent objects; so < is valid, while <= is not.
If you look at the example implementation, < is used for checking if the next element is less than the previous one:
if (*next < *first)
return next;
If it is, then the order is broken, and the function returns. I. e. the logic is reversed - the algorithm does not terminate if the next element is equal to the previous.
Why does STL work with a comparison function that is strict weak ordering? Why can't it be partial ordering?
A partial order would not be sufficient to implement some algorithms, such as a sorting algorithm. Since a partially ordered set does not necessarily define a relationship between all elements of the set, how would you sort a list of two items that do not have an order relationship within the partial order?
Simply, a strict weak ordering is defined as an ordering that defines a (computable) equivalence relation. The equivalence classes are ordered by the strict weak ordering: a strict weak ordering is a strict ordering on equivalence classes.
A partial ordering (that is not a strict weak ordering) does not define an equivalence relation, so any specification using the concept of "equivalent elements" is meaningless with a partial ordering that is not a strict weak ordering. All STL associative containers use this concept at some point, so all these specifications are meaningless with a partial ordering that is not a strict weak ordering.
Because a partial ordering (that is not a strict weak ordering) does not necessarily define any strict ordering, you cannot "sort elements" in the common sense according to partial ordering (all you can do is a "topological sort" which has weaker properties).
Given
a mathematical set S
a partial ordering < over S
a value x in S
you can define a partition of S (every element of S is either in L(x), I(x) or G(x)):
L(x) = { y in S | y<x }
I(x) = { y in S | not(y<x) and not(x<y) }
G(x) = { y in S | x<y }
L(x) : set of elements less than x
I(x) : set of elements incomparable with x
G(x) : set of elements greater than x
A sequence is sorted according to < iff for every x in the sequence, elements of L(x) appear first in the sequence, followed by elements of I(x), followed by elements of G(x).
A sequence is topologically sorted iff for every element y that appears after another element x in the sequence, y is not less than x. It is a weaker constraint than being sorted.
It is trivial to prove that every element of L(x) is less than any element of G(x). There is no general relation between elements of L(x) and elements of I(x), or between elements of I(x) and elements of G(x). However, if < is a strict weak ordering, than every element of L(x) is less than any element of I(x), and than any element of I(x) is less than any element of G(x).
If < is a strict weak ordering, and x<y then any element of L(x) U I(x) is less then any element I(y) U G(y): any element not greater than x is less than any element not less that y. This does not necessarily hold for a partial ordering.
Quoting the answer given here:
Because internally, these algorithms implement "is equal to" as !(a < b) && !(b < a).
If you used <= to implement the less-than operator, then the above
would return false when a == b. A broken equality check will screw up
nearly any algorithm.
Similarly, they implement "is not equal to" as (a < b) || (b < a)
, and once again, if you implemented the < operator using <=, then it
will return true when they are equal to each other, when in fact they
are not equal. So the equality check is broken in both directions.
The whole point of limiting the library to a less-than operator is
that all of the logical operators can be implemented in terms of it:
<(a, b): (a < b)
<=(a, b): !(b < a)
==(a, b): !(a < b) && !(b < a)
!=(a, b): (a < b) || (b < a)
>(a, b): (b < a)
>=(a, b): !(a < b)
This works as long as your provided operator meets the conditions of a
strict weak ordering. The standard <= and >= operators do not.
You cannot perform binary search with partial ordering. You cannot create a binary search tree with partial ordering. What functions/datatypes from algorithm need ordering and can work with partial ordering?