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

时间:2017-08-29 01:37:53

标签: c++ sorting std

在std :: sort中,您可以提供第三个参数,该参数是对列表进行排序的基础。如果您希望第一个参数首先出现,则返回true。如果您希望第一个参数首先出现,则返回false。我遇到的问题是我的谓词函数应该是一个“无效的比较器”,我已经将它缩小到它不符合以下要求的事实:

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

我见过一些术语,例如std :: sort需要“严格的弱排序”。除了2个地方,我得到的关于这些主题的所有其他页面似乎都是技术论文,我无法理解。我能理解的是:

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是相等的,在这种情况下我不关心哪一个在另一个之前(之前的一个会让我快乐),为什么我不能回到真实的争论1先来?

我还要问我的程序实际上是怎么知道它是一个无效的比较器然后我认为它可能只是在比较函数返回true时检查arg1和arg2是否相等。

5 个答案:

答案 0 :(得分:4)

比较功能只是模拟一个&#34;小于&#34;运营商。考虑<运算符如何适用于基本类型,如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表示您希望在a之前订购b。因此,如果不是这样,请返回false,因为您希望在b之前订购a,或者因为他们的订单无关紧要。

如果您在论据相同时返回true,那么您说abb,而您希望a var extent = [0, 0, 418, 600]; var projection = new ol.proj.Projection({ code: 'xkcd-image', units: 'pixels', extent: extent }); var map = new ol.Map({ layers: [ new ol.layer.Tile({ source: new ol.source.OSM() }), new ol.layer.Group({ layers: [ new ol.layer.Image({ source: new ol.source.ImageStatic({ url: 'http://localhost:2265/images3/test2.png', projection: projection, imageExtent: extent, }) }), new ol.layer.Image({ source: new ol.source.ImageStatic({ url: 'http://localhost:2265/images2/test1.png', projection: projection, imageExtent: extent, }) }) ] }) ], target: 'map', view: new ol.View({ projection: projection, center: ol.extent.getCenter(extent), zoom: 2, maxZoom: 8 }) }); var layers = map.getLayers().getArray(); var frame = 1; setInterval(function () { layers[frame].setVisible = false; frame = (frame + 1) % 2; layers[frame].setVisible = true; },500); {{1}} 1}},这是一个矛盾。

答案 1 :(得分:3)

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等等)和向量范围之外的索引(在这种情况下为负索引)。

答案 2 :(得分:1)

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

答案 3 :(得分:1)

当元素计数大于 _S_threshold (在STL中,默认值为16)时, std :: sort()将使用快速排序

以下代码是快速排序中的 __ unguarded_pa​​rtition 函数。

  /// 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;
      }
    }

如果arg1 == arg2,则compare函数返回true,则 __ first 迭代器将继续向右移动,程序将超出范围并导致coredump。

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

因此,如果arg1 == arg2,则比较函数必须返回false。

您还可以看到 <<有效STL >>,第21项:始终使比较函数对相等的值返回false。

答案 4 :(得分:0)

如果不深入数学,只需使用&#39;&lt;&#39;运营商(或者如果您愿意的话,&#39;&gt;&#39;但是&#39;&lt;&#39;通常用于解释严格的弱排序和分拣机的实施。

这个想法基本上是在实际编程中a < b == falseb < a == false然后是a == b