【问题标题】:How to sort a List<String> in descending order as fast as possible如何尽可能快地按降序对 List<String> 进行排序
【发布时间】:2019-02-20 20:31:35
【问题描述】:

我正在尝试找到一种方法来尽可能快地对列表进行排序。我知道我将使用的“桶排序”(我认为?)。但在这样做之前。我想我首先在寻找最快的干净算法,然后在桶排序中使用它?

字符串如下所示,我在循环中添加了 100.000 个元素:

-0,awb/aje - ddfas/asa - asoo/qwa
-1,awb/aje - ddfas/asa - asoo/qwa
-2,awb/aje - ddfas/asa - asoo/qwa

所以我想对第一个以逗号分隔的参数进行降序排序,它是一个双精度参数:-0、-1、-2 等。

我尝试了 3 种方法,其中只有方法 1 真正正确排序。方法 2 和 3 不能完全正确地按数字降序排序。

因此,正确排序的方法 1 可以在 30 秒内完成。 问题是我将有大约 300 万个元素,而不仅仅是 100.000,就像在这个例子中那样,这至少需要 900 秒或更长时间。

我的问题是我们如何才能尽可能快地对 100.000 个或更多正确的 300 万个元素进行排序? 运行:sortingtestBENCHMARKS() 将显示结果

    public void sortingtestBENCHMARKS()
    {
        List<String> dataLIST = new List<String>(); List<String> sortedLIST = new List<String>(); String resultString = ""; int index = 0;
        DateTime starttime = DateTime.Now; DateTime endtime = DateTime.Now; TimeSpan span = new TimeSpan();
        for (double i = 0; i < 100000; i+=1)
        {
            dataLIST.Add("-" + i + "," + "awb/aje" + " - " + "ddfas/asa" + " - " + "asoo/qwa");
        }
        dataLIST = shuffle(dataLIST);

        /*--------------------------------------------------------------------------*/
        //APPROACH 1: 30 seconds (Sorts correctly in descending order)
        starttime = DateTime.Now;
        dataLIST = sortLIST(dataLIST);
        endtime = DateTime.Now;
        span = endtime - starttime;
        resultString = "Approach 1: " + span.TotalSeconds;
        dataLIST = shuffle(dataLIST);

        /*--------------------------------------------------------------------------*/
        //APPROACH 2: 55 seconds (Sorts INcorrectly in descending order)
        starttime = DateTime.Now;
        for (int i = 0; i < dataLIST.Count; i++) 
        {
            index = sortedLIST.BinarySearch(dataLIST[i]);
            if (index < 0)
            {
                sortedLIST.Insert(~index, dataLIST[i]);
            }
        }
        endtime = DateTime.Now;
        span = endtime - starttime;
        resultString = resultString + "\nApproach 2: " + span.TotalSeconds;

        /*--------------------------------------------------------------------------*/
        //APPROACH 3: 2 seconds (Sorts INcorrectly in descending order)
        starttime = DateTime.Now;
        dataLIST.Sort(); //1.6 seconds
        endtime = DateTime.Now;
        span = endtime - starttime;
        resultString = resultString + "\nApproach 3: " + span.TotalSeconds;

        /*--------------------------------------------------------------------------*/

        MessageBox.Show("Elapsed Times:\n\n" + resultString);
    }
    List<String> sortLIST(List<String> theLIST)
    {
        System.Threading.Thread.CurrentThread.CurrentCulture = new System.Globalization.CultureInfo("en-US");
        theLIST.Sort(new Comparison<String>((a, b) =>
        {
            int result = 0;
            double ad = 0;
            double bd = 0;
            NumberFormatInfo provider = new NumberFormatInfo();
            provider.NumberGroupSeparator = ",";
            provider.NumberDecimalSeparator = ".";
            provider.NumberGroupSizes = new int[] { 5 };
            ad = Convert.ToDouble(a.Replace("a", "").Replace("c", "").Split(',')[0], provider);
            bd = Convert.ToDouble(b.Replace("a", "").Replace("c", "").Split(',')[0], provider);
            if (ad < bd)
            {
                result = 1;
            }
            else if (ad > bd)
            {
                result = -1;
            }
            return result;
        }));
        return theLIST;
    }
    List<String> shuffle(List<String> list)
    {
        var randomizedList = new List<String>();
        var rnd = new Random();
        while (list.Count != 0)
        {
            var index = rnd.Next(0, list.Count);
            randomizedList.Add(list[index]);
            list.RemoveAt(index);
        }
        return randomizedList;
    }

【问题讨论】:

  • 你想达到什么目的?你不能使用 Arrays.sort()/Collections.sort()/TreeSet 和适当的比较器(例如 Comparator::reverseOrder)吗?
  • 是的,我的代码中的方法 1 确实使用了使用比较器的排序算法。但我正在寻找是否有更智能/更好的解决方案,因为它需要 30 秒。仅使用标准的 Collections.Sort() 方法只需 2 秒,但它并没有按照我们所知的完全正确的降序排序。
  • 您应该始终包含您正在使用的语言的标签!一旦没有语言,页面根本不会突出显示代码。
  • (是的,你是对的,我添加了 c# 作为问题的标签)
  • 你实际上是在比较不同的东西,你写的那个“比较器”正在做很多低效的事情。此外,如果您在排序时将所有内容都转换为双精度,那么为什么列表首先不是双精度的?

标签: c# list performance sorting


【解决方案1】:

在我看来,您可以在 , 字符上拆分您的字符串,从拆分数组中的第一项中剥离 -,然后在该结果上使用 OrderBy

var sorted = dataLIST.OrderBy(i => double.Parse(i.Split(',')[0].TrimStart('-'))).ToList();

我复制了您的代码,然后使用了您拥有的一种工作方法,并将其与在上述拆分字符串方法上运行OrderBy 进行了比较。 OrderBy/Split 方法快了 30 多倍。

public static void sortingtestBENCHMARKS()
{
    var dataLIST = new List<string>();

    // Create the list
    for (var i = 0; i < 100000; i ++)
    {
        dataLIST.Add("-" + i + "," + "awb/aje" + " - " + "ddfas/asa" + " - " + "asoo/qwa");
    }

    // Shuffle the list
    dataLIST = shuffle(dataLIST);

    // Make two copies of the same shuffled list
    var copy1 = dataLIST.ToList();
    var copy2 = dataLIST.ToList();

    // Use a stopwatch for measuring time when benchmark testing
    var stopwatch = new Stopwatch();

    /*--------------------------------------------------------------------------*/
    //APPROACH 1: 2.83 seconds (Sorts correctly in descending order)
    stopwatch.Start();
    copy2 = sortLIST(copy2);
    stopwatch.Stop();
    Console.WriteLine($"sortLIST method: {stopwatch.Elapsed.TotalSeconds} seconds");

    /*--------------------------------------------------------------------------*/
    //APPROACH 2: 0.09 seconds (Sorts correctly in descending order)
    stopwatch.Restart();
    copy1 = copy1.OrderBy(i => double.Parse(i.Split(',')[0].TrimStart('-'))).ToList();
    stopwatch.Stop();
    Console.WriteLine($"OrderBy method:  {stopwatch.Elapsed.TotalSeconds} seconds");

    // Ensure that the lists are sorted identically
    Console.WriteLine($"Lists are the same: {copy1.SequenceEqual(copy2)}");
}

输出

【讨论】:

  • 哇!!!这完全是惊人的。革命性的:) 我刚刚测试了代码,可以产生相同的结果。所以 OrderBy/Split 快得多。我非常感谢你这个方法 Rufus!我现在将使用这段代码:)
【解决方案2】:
var sortedList = theLIST.OrderByDescending(s=>s);

【讨论】:

  • 那个 ToList 是多余的。
  • 是的,它是多余的。
  • 它可能已经过编辑,但他们有一个非常具体的排序
  • 似乎这种方法并没有按降序“完全”正确排序。我刚才试过了。
【解决方案3】:

您应该尽可能优化内部循环,因为大部分处理时间都花在了那里。我建议您自己实现字符串标记器,因为您只需要第一个标记并且字符串非常统一。您可能进行的第二个优化是将所有数字乘以 -1,因此以相反的顺序对列表进行排序很简单。像这样的:

private static double getNumberFromString(String s){
    int posFirstComma=0;
    for (; posFirstComma<s.length() && s.charAt(posFirstComma)!=','; posFirstComma++);
    return Convert.toDouble(s.subString(0, posFirstComma)*(-1);
}
myData.sort(new Comparision<String>((a,b)=> getNumberFromString(a)-getNumberFromString(b));

我个人不会触及库本身中的排序算法,因为它已经彻底优化。只需优化 for 循环中的所有内容。

【讨论】:

  • 这一行不应该一样吗?: return Convert.ToDouble(s.Substring(0, s.IndexOf(','))) * -1;但是使用代码在 getNumberFromString 下划线的地方无法编译?: dataLIST.Sort(new Comparison((a, b) => getNumberFromString(a) - getNumberFromString(b)));
  • 乘以(-1)不应该工作,因为我会有正负双打
  • 它无法编译,因为 Comparator 返回一个 int,而我返回的是 double。这是一件小事,因为您始终可以将双打与 if 子句进行比较并返回 1 和 -1 就像在您的代码中一样(我认为您也需要考虑它们何时相等,以防您的数字不是唯一的)。我只是给你一种伪代码来演示这个想法。是的 s.indexOf() 也有效,sr 忘记了))。如果它们不是全部负数,则只需使用库中的某些函数按相反顺序排序。缓慢之处在于您的比较器。这不太可能是库排序代码的原因。
猜你喜欢
  • 2017-11-02
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2021-07-26
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多