c++ 为什么std::sort compare函数在参数相等时必须返回false?

xa9qqrwz  于 2023-06-07  发布在  其他
关注(0)|答案(6)|浏览(162)

在std::sort中,你可以提供第三个参数,这是它如何对列表进行排序的基础。如果你想让第一个参数先出现,那么就返回true。如果你想让第二个参数先出现,你就返回false。我遇到了一个问题,我的 predicate 函数被认为是一个“无效的比较器”,我已经把它缩小到它不满足以下要求的事实:

if arg1 == arg2, compare function MUST return false.

我曾经见过一些术语,比如std::sort要求“严格弱排序”。除了两个地方,我得到的关于这些主题的所有其他页面似乎都是技术论文,我看不懂。我能理解的是:

In weak ordering some elements "may be tied" with each other.

但对我来说这也是“偏序集”的含义,它是:

"there may be pairs of elements for which neither element precedes the other"

此外,我不明白“严格”在其中任何一个中意味着什么。
抛开我对序理论术语的困惑不谈,我的问题是,如果在比较函数中,参数1和参数2相等,并且在这种情况下,我不在乎哪个在另一个之前(任何一个在前面都会让我高兴),为什么我不能返回true以使参数1在前面?
我还想问我的程序是如何知道它是一个无效的比较器的,但后来我想它可能只是在比较函数返回true时检查arg1和arg2是否相等。

nimxete2

nimxete21#

compare函数只是模拟了一个“小于”运算符。考虑一下<运算符是如何处理像int这样的原始类型的:

int a = 1, b = 2;     a < b == true      a is less than b
int a = 2, b = 1;     a < b == false     a is not less than b, because a is greater than b
int a = 1, b = 1;     a < b == false     a is not less than b, because a equals b

返回true意味着您希望ab之前排序。因此,如果不是这种情况,则返回false,因为您希望ba之前排序,或者因为它们的顺序无关紧要。
如果你在参数相等时返回true,那么你是说你希望ab之前,你希望ba之前,这是一个矛盾。

pbossiut

pbossiut2#

1.从代码Angular 看

当elements count大于**_S_threshold**(STL中默认值为16)时,std::sort()会使用quicksort
下面的代码是快速排序中的**__unguarded_partition**函数。

/// This is a helper function...
  template<typename _RandomAccessIterator, typename _Tp, typename _Compare>
    _RandomAccessIterator
    __unguarded_partition(_RandomAccessIterator __first,
             _RandomAccessIterator __last,
             const _Tp& __pivot, _Compare __comp) 
   {
      while (true)
      {
        while (__comp(*__first, __pivot))
          ++__first;
        --__last;
        while (__comp(__pivot, *__last))
          --__last;
        if (!(__first < __last))
          return __first;
        std::iter_swap(__first, __last);
        ++__first;
      }
    }

如果arg 1 == arg 2,compare函数返回true,那么**__first**迭代器将继续向右移动,程序将超出范围并导致coredump。

while (__comp(*__first, __pivot))
    ++__first;

因此,如果arg 1 == arg 2,则比较函数必须返回false。

2.从算法逻辑Angular 看

如果比较函数使用运算符<=,则对于相等的值返回true。如果要测试10 B是否等于10A,则自然会使用比较功能。在这个例子中,这是运算符<=。Tt检查这个表达式是否为真:

!(10A<= 10B)&&!(10B<= 10A) //test 10Aand 10B for equivalence

10A和10 B都是10,所以10A <= 10 B显然是正确的。同样清楚地,10 B <= 10A。上述表达式因此简化为

!(true)&&!(true)

这就简化为

false && false

这是完全错误的。也就是说,测试得出结论,IOA和IOB不等同,因此不相同。此外,任何相等值返回true的比较函数都会做同样的事情。根据定义,相等的值不是相等的!是不是很酷?
你还可以看到**<< Effective STL>>,Item 21:Always have comparison functions return false for equal values.**

vuktfyat

vuktfyat3#

std::sort使用的算法未指定。通过使用不符合标准要求的比较函数,您打破了算法的假设,并导致未定义的行为。
看看这个噪声比较函数的输出会发生什么,它使用<=(不是有效的比较器) 而不是<(有效)
http://coliru.stacked-crooked.com/a/34e4cef3280f3728
在输出中,我打印了一个递增的变量 (供参考,以指出算法何时失控),后跟第一个参数的值及其在向量中的位置,然后是第二个参数及其在向量中的位置。示例如下所示:

124: 2@12 <= 4@7

这意味着这是比较函数的第124次调用,它正在比较索引12处的值2和索引7处的值4。
事情从第37行开始变得疯狂

37: 0@0 <= 0@-1
38: 0@0 <= 144@-2
39: 0@0 <= 0@-3
40: 0@0 <= 192@-4

它比较我甚至没有插入到向量中的值(144,192等)以及向量范围之外的索引(在本例中为负索引)。

23c0lvtd

23c0lvtd4#

为了解释Benjamin Lindley的答案,考虑std::sort使用Hoare类型分区方案的快速排序的典型情况。使用compare(value,pivot)扫描左侧的values < pivot以进行比较,而使用compare(pivot,value)扫描右侧的values > pivot。请注意,快速排序分区可能依赖于这样的事实,即左扫描或右扫描在遇到值== pivot时停止,并且不会继续扫描该扫描上的pivot。如果用户提供的compare函数在这样的比较中返回true(当value == pivot时为true),则扫描可以继续超出正在排序的数组或向量的边界,这显然是Benjamin Lindley的测试用例中发生的情况。

jobtbby3

jobtbby35#

不需要深入研究数学,只需要使用'<'运算符(如果愿意,也可以使用'>')就可以比较两个变量。然而,“<”通常用于解释“严格弱排序”和实现排序器。
这个想法基本上是在实际编程中,如果a < b == falseb < a == false,那么a == b

bqujaahr

bqujaahr6#

TL;DR:为什么std::sort compare函数在参数相等时必须返回false?

它必须返回false,因为不需要对相等的参数进行排序。

说明

双向比较运算符函数检查是否要在第二个数字之前返回第一个数字。如果是,则返回true,如果不是,则返回false
std::sort和许多其他库函数使用这样的运算符函数。
例如。

std::sort(my_container.begin(), my_container.end(), [](const MY_CLASS* lhs, const MY_CLASS* rhs) -> bool
{
    return lhs->m_level < rhs->m_level;
});

然而,当比较函数用作此类库函数中的参数时,此类库函数习惯上基于比较函数的true响应来“动作”。因此,特别是对于std::sortstd::sort会做一些事情(即。当参数为真时,执行重新排序)。
所以一般来说,当比较函数用作std lib算法的参数时,如果算法希望它做一些事情,它应该返回true。反之亦然,当它不应该做任何事情时,它应该返回false

相关问题