使用IComparer <string>对16个元素进行排序至少进行了100000次比较

时间:2018-02-06 08:55:33

标签: c# sorting icomparer

当我尝试使用IComparer<string>进行排序时,至少需要进行100000次比较。

为什么?

public class PeriodComparer: IComparer<string>
{
  int num = 0;

  public int Compare(string x, string y)
  {
    var year1 = int.Parse(x.Substring(2));
    var year2 = int.Parse(y.Substring(2));

    if (year1 < year2)
        return -1;

    if (year1 > year2)
        return 1;

    var season1 = x.Substring(0, 2);

    return season1 == "VT" ? -1 : 1;
  }
}

我尝试用它来对数组字符串进行排序,如

var strings = new []
    {"VT2010", "HT2010",
    "VT2011", "HT2011",
    "VT2012", "HT2012",
    "VT2013", "HT2013",
    "VT2014", "HT2014",
    "VT2015", "HT2015",
    "VT2016", "HT2016",
    "VT2017", "HT2017"};


var comparer = new PeriodComparer();
var orderedPeriodNames = strings.OrderBy(x => x, comparer).ToList();

期望字符串先按年份排序,然后再按VT和HT排序。 (这意味着此特定情况下的输入已经排序)。

然而,执行停滞不前,所以我在比较函数中设置了一个计数器,如

public class PeriodComparer: IComparer<string>
{
  int num = 0;

  public int Compare(string x, string y)
  {
    if (++num >= 100000)
    {
      // setting a breakpoint here
    }

    var year1 = int.Parse(x.Substring(2));
    var year2 = int.Parse(y.Substring(2));

    if (year1 < year2)
        return -1;

    if (year1 > year2)
        return 1;

    var season1 = x.Substring(0, 2);

    return season1 == "VT" ? -1 : 1;
  }
}

断点被击中,因此至少需要进行100000次比较。

4 个答案:

答案 0 :(得分:2)

如果您将一个调试语句添加到比较器并仅在两个字符串上调用它,您可以看到发生了什么。

你得到这个输出:

Comparing VT2010 to VT2010
Comparing VT2010 to HT2010
Comparing VT2010 to VT2010

一遍又一遍地重复。显然它无法弄清VT2010在结果中应该去哪里,因为每次它认为它找到了正确的位置并进行了最后的比较,结果证明它离右边太远了,因为仍然{{1} }。

正确commented by Ivan Stoev

  

请注意,可以为两个相同的项目调用比较器。

如果添加行

VT2010 < VT2010

在比较方法的顶部,排序算法将能够找出if(x == y) { return 0; } VT2010VT2010相比的正确相对顺序。

答案 1 :(得分:2)

你的季节比较可能需要一些调整。类似的东西:

public class PeriodComparer : IComparer<string>
{
    int num = 0;

    public int Compare(string x, string y)
    {
        if (++num >= 100000)
        {
            Console.WriteLine(num);
        }

        var year1 = int.Parse(x.Substring(2));
        var year2 = int.Parse(y.Substring(2));

        if (year1 < year2)
            return -1;

        if (year1 > year2)
            return 1;

        var season1 = x.Substring(0, 2);
        var season2 = y.Substring(0, 2);

        if (season1 == "VT" && season2 != "VT")
            return -1;
        if (season2 == "VT" && season1 != "VT")
            return 1;

        return StringComparer.InvariantCulture.Compare(season1, season2);
    }
}

这将确保排序结果一致

要进行比较,请使用“HJ2014”和“HT2014”的输入调用现有代码。它将返回1.现在用“HT2014”和“HJ2014”调用它 - 现在它将仍然返回1.这是意料之外的。

你基本上已经说过了:

  

“HJ2014”大于“HT2014”

  

“HJ2014”小于“HT2014”

显然,这两者都不可能是真的。因此,这会“混淆”排序算法,并使其旋转。

同样,您的代码也会声明:

  

“VT2014”小于“VT2014”

这显然是假的。这导致问题,因为OrderBy使用了QuickSort,因此可以将条目与自身进行比较。

答案 2 :(得分:2)

比较任意项目时,请说AB,我们必须确保

  A == A
  ...
  whenever A > B then B < A

请注意,在您的情况下,这些规则已损坏;此外,从不威胁字符串相等;简单的例子

  var A = "VT2018";

  // Expected 0, Actual -1
  int result = (new PeriodComparer()).Compare(A, A);

正确准确(在处理public类时,我们必须预期任何输入)实现:

  public int Compare(string x, string y) {
    // Special cases: equal strings, nulls
    if (string.Equals(x, y))
      return 0;
    else if (string.Equals(null, x))  // null is smaller than any string
      return -1;
    else if (string.Equals(null, y)) 
      return 1;

    // Suffixes
    // TrimStart('0'): we want "0003" == "3" < "10":  
    string suffixX = x.Length <= 2 ? "" : x.Substring(2).TrimStart('0');
    string suffixY = y.Length <= 2 ? "" : y.Substring(2).TrimStart('0');

    // Natural order: Length first (2000 > 900)...
    if (suffixX.Length > suffixY.Length)
      return 1;
    else if (suffixX.Length < suffixY.Length)
      return -1;

    // ...Lexicograhical next: 2040 > 2030
    int result = string.Compare(suffixX, suffixY);

    if (result != 0)
      return result;

    // Equal Suffixes; compare prefixes
    string prefixX = x.Length <= 2 ? x : x.Substring(0, 2);
    string prefixY = y.Length <= 2 ? y : y.Substring(0, 2);

    if (prefixX == "VT" && prefixY != "VT")
      return -1;
    else if (prefixY == "VT" && prefixX != "VT")
      return 1;

    return string.Compare(prefixX, prefixY);
  } 

答案 3 :(得分:0)

你不适应'tie-breaker'比较(子串“VT”与“HT”)相同的情况。