簡體   English   中英

Array.Sort / IComparable有時在首次調用時無法正確排序

[英]Array.Sort / IComparable sometimes doesn't sort correctly on first call

我正在嘗試解決11321-排序! 分類!! 和排序! 使用C#,直接從cpp和Java轉換我的解決方案(以及其他人的解決方案)。

我的問題是listName.Sort()Array.Sort(listName, ...Comparer.Create()...)在第一次通過時未正確排序輸出。 我必須調用它兩次才能對其進行正確排序。

在某些情況下,我在調用Array.Sort時在CompareTo()中手動設置斷點,故意在閉包內添加對列​​表的引用,這樣我就可以觀察值的排序,直到Array.Sort之后它才能正確排序。 ()方法返回,然后在其中看到一些值返回錯誤的順序。

我正在使用從uDebug進行的Morass測試用例進行測試,我得到的不正確排序結果的一個示例在輸出的10919行上:

Accepted            My Output
10919   457         10919   461
10920   461         10920   457

如您所見,數字461和457應該按其模500值的升序排序,分別為461和457。 如果我再次在下面的代碼中再次調用sort方法,那么我最終將獲得正確的輸出。

我想我的問題是,為什么會這樣? 我的實現有什么問題嗎? 我的實現幾乎是接受的Java或cpp代碼的一對一翻譯。 請注意,我還嘗試過使用LINQ的OrderBy(),它會產生不同的結果,但在調用足夠多的時間時最終會得出正確的結果。

我具有以下Number類以及相應的IComparable實現:

class Number : IComparable<Number>
{
    public int Value { get; }
    public int Mod { get; }
    public bool IsOdd { get; }

    public Number(int val, int mod)
    {
        Value = val;
        Mod = mod;
        IsOdd = val % 2 != 0;
    }

    public int CompareTo(Number other)
    {
        var leftVal = Value;
        var leftMod = Mod;
        var rightVal = other.Value;
        var rightMod = other.Mod;

        var leftOdd = IsOdd;
        var rightOdd = other.IsOdd;

        if (leftMod < rightMod) return -1;
        else if (leftMod > rightMod) return 1;
        else
        {
            if (leftOdd && rightOdd)
            {
                return leftVal > rightVal ? -1 : 1;
            }
            else if (!leftOdd && !rightOdd)
            {
                return leftVal > rightVal ? 1 : -1;
            }
            else if (leftOdd)
            {
                return -1;
            }
            else// (rightOdd)
            {
                return 1;
            }
        }
    }
}

而我的主要方法是:

public static void Main(string[] args)
    {
        while (true)
        {
            var settings = Console.ReadLine().Split(' ');
            var N = int.Parse(settings[0]);
            var M = int.Parse(settings[1]);

            if (N == 0 && M == 0) break;

            Console.WriteLine($"{N} {M}");
            var output = new List<Number>();

            var i = 0;
            while (i < N)
            {
                var line = Console.ReadLine();
                var val = int.Parse(line);
                var mod = val % M;
                output.Add(new Number(val, mod));
                i++;
            }

            output.Sort();
            // uncomment to produce acceptable answer
            // output.Sort();

            foreach (var line in output)
            {
                Console.WriteLine(line.Value);
            }
        }

        Console.WriteLine("0 0");
    }

編輯1:

請注意,我正在將stdin和stdout從文件/重定向到StringBuilder,這樣我可以自動化測試。

    static void Main(string[] args)
    {
        var builder = new StringBuilder();
        var output = new StringWriter(builder);
        Console.SetOut(output);

        var solution = File.ReadAllText("P11321_Outputs");
        var problem = new StreamReader("P11321_Inputs");
        Console.SetIn(problem);

        P11321_1.Main(args);
    }

編輯2:這是測試案例中發生奇怪行為的一部分。 一個具體的再現步驟是,如果將測試用例更改為僅包含38個項目,並從輸入中刪除11個,則正確排序為457和461。

輸入:

39 500
-121
582
163
457
-86
-296
740
220
-867
-333
-773
11
-446
-259
-238
782
461
756
-474
-21
-358
593
548
-962
-411
45
-604
-977
47
-561
-647
926
578
516
382
-508
-781
-322
712
0 0

輸出:

39 500
-977
-474
-962
-446
-411
-867
-358
-333
-322
-296
-781
-773
-259
-238
-647
-121
-604
-86
-561
-21
-508
11
516
45
47
548
578
582
593
163
712
220
740
756
782
382
926
457
461
0 0

您設法檢查了布爾測試中的所有情況,除非值相等。 排序算法不僅需要知道元素是否大於或小於彼此,還需要知道它們是否彼此相等。

  if (leftMod < rightMod)
    return -1;
  else if (leftMod > rightMod)
    return 1;
  else
  {
    if (leftVal == rightVal)
    {
      return 0; // need this so you don't orphan an element when tested against itself
    }
    if (leftOdd && rightOdd)
    {
      return leftVal > rightVal ? -1 : 1;
    }
    else if (!leftOdd && !rightOdd)
    {
      return leftVal > rightVal ? 1 : -1;
    }
    else if (leftOdd)
    {
      return -1;
    }
    else// (rightOdd)
    {
      return 1;
    }
  }

對於Mark Benningfield的回答加倍,我想提出一個概念證明,說明為什么在自定義比較器的實現中包括相等性很重要。 不僅存在錯誤結果的風險,而且還存在從未獲得結果的風險!

嘗試使用錯誤的比較器僅對兩個數字(2,1)進行排序:

class BuggyComparer : IComparer<int>
{
    public int Compare(int x, int y) => x < y ? -1 : 1; // Equality?
}

var source = new int[] { 2, 1 };
var sorted = source.OrderBy(n => n, new BuggyComparer());
Console.WriteLine(String.Join(", ", sorted)); // Infinite loop

該程序沒有終止,因為無法完成排序。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM