【问题标题】:Why is bubble sort running faster than selection sort?为什么冒泡排序比选择排序运行得快?
【发布时间】:2021-11-24 10:50:53
【问题描述】:

我正在做一个比较时间泡和选择排序的项目。我制作了两个单独的程序并将它们组合成一个程序,现在冒泡排序比选择排序运行得快得多。我检查以确保代码不只是因为某些转换错误而给我 0 并且按预期运行。我正在使用System.Diagnostics; 来测量时间。我还检查了机器没有问题,我在Replit上运行它并得到了类似的结果。

{
    class Program
    {
       public static int s1 = 0;
       public static int s2 = 0;
        static decimal bubblesort(int[] arr1)
        {
            int n = arr1.Length;
            var sw1 = Stopwatch.StartNew();
            for (int i = 0; i < n - 1; i++)
            {
                for (int j = 0; j < n - i - 1; j++)
                {
                    if (arr1[j] > arr1[j + 1])
                    {
                        int tmp = arr1[j];
                        // swap tmp and arr[i] int tmp = arr[j];
                        arr1[j] = arr1[j + 1];
                        arr1[j + 1] = tmp;
                        s1++;
                    }
                }
            }
            sw1.Stop();
            // Console.WriteLine(sw1.ElapsedMilliseconds);
            decimal a = Convert.ToDecimal(sw1.ElapsedMilliseconds);
            return a;
        }

        static decimal selectionsort(int[] arr2)
        {
            int n = arr2.Length;
            var sw1 = Stopwatch.StartNew();
           // for (int e = 0; e < 1000; e++)
           // {

                for (int x = 0; x < arr2.Length - 1; x++)
                {
                    int minPos = x;
                    for (int y = x + 1; y < arr2.Length; y++)
                    {
                        if (arr2[y] < arr2[minPos])
                            minPos = y;
                    }
                    if (x != minPos && minPos < arr2.Length)
                    {
                        int temp = arr2[minPos];
                        arr2[minPos] = arr2[x];
                        arr2[x] = temp;
                        s2++;
                    }
                }

          //  }
            sw1.Stop();
           // Console.WriteLine(sw1.ElapsedMilliseconds);
            decimal a = Convert.ToDecimal(sw1.ElapsedMilliseconds);
            return a;
        }
        static void Main(string[] args)
        {
            Console.WriteLine("Enter the size of n");
            int n = Convert.ToInt32(Console.ReadLine());
            Random rnd = new System.Random();
            decimal bs = 0M;
            decimal ss = 0M;
            int s = 0;
            int[] arr1 = new int[n];
            int tx = 1000; //tx is a variable that I can use to adjust sample size
            decimal tm = Convert.ToDecimal(tx);

            for (int i = 0; i < tx; i++)
            {

                for (int a = 0; a < n; a++)
                {
                    arr1[a] = rnd.Next(0, 1000000);
                }

                

                ss += selectionsort(arr1);
                bs += bubblesort(arr1);
            }

            bs = bs / tm;
            ss = ss / tm;

            Console.WriteLine("Bubble Sort took " + bs + " miliseconds");
            Console.WriteLine("Selection Sort took " + ss + " miliseconds");


        }
    }
}

发生了什么事?是什么导致冒泡排序变快或减慢选择排序?我该如何解决这个问题?

我发现问题在于,除了针对样本大小的 1000 次运行之外,选择排序在每个方法运行中循环 1000 次,导致该方法的性能明显低于冒泡排序。谢谢大家的帮助,也感谢 TheGeneral 向我展示了基准测试工具。此外,作为参数给出的数组是副本而不是引用,因为手动运行循环向我显示冒泡排序正在完成它的工作,而不是对已经排序的数组进行排序。

【问题讨论】:

  • 衡量性能几乎是一门艺术。您需要多次运行,并且需要混淆它们运行的​​顺序,这样一个就不会“热身”另一个。还要确保它不是调试版本。还要确保使用 相同 数据集。这里有很多很多这样的帖子解释更多
  • 冒泡排序在已经排序的数组上运行,所以它不需要对任何东西进行排序。在执行之前尝试深度复制输入数组并在单独的实例上运行函数。
  • 顺便说一下,除了对已经排序的数组进行排序之外,您的冒泡排序还没有正确实现——它不是自适应的。正确实现的冒泡排序不会重新排序已经排序的数组,如果没有发生交换,它也不会检查第一个完整循环 - 即使没有交换,你的仍然会执行 O(n^2) 检查正在发生。正确实施的冒泡排序通常会击败选择排序。查看这个有用的网站:toptal.com/developers/sorting-algorithms

标签: c# sorting bubble-sort selection-sort


【解决方案1】:

看起来您正在将排序后的数组传递给冒泡排序。因为数组是通过引用传递的,所以您对数组执行的排序是在编辑数组的相同内容,这些内容最终将传递给冒泡排序。

创建第二个数组并将第二个数组传递给冒泡排序。

【讨论】:

    【解决方案2】:

    要解决您最初的问题,您只需要复制您的数组,您可以使用 ToArray() 轻松完成此操作:

    从 IEnumerable 创建一个数组。

    ss += selectionsort(arr1.ToArray());
    bs += bubblesort(arr1.ToArray());
    

    但是,让我们学习如何使用 BenchmarkDotNet 进行更可靠的基准测试:

    给定

    public class Sort
    {
       public static void BubbleSort(int[] arr1)
       {
          int n = arr1.Length;
          for (int i = 0; i < n - 1; i++)
          {
             for (int j = 0; j < n - i - 1; j++)
             {
                if (arr1[j] > arr1[j + 1])
                {
                   int tmp = arr1[j];
                   // swap tmp and arr[i] int tmp = arr[j];
                   arr1[j] = arr1[j + 1];
                   arr1[j + 1] = tmp;
                }
             }
          }
       }
    
       public static void SelectionSort(int[] arr2)
       {
          int n = arr2.Length;
          for (int x = 0; x < arr2.Length - 1; x++)
          {
             int minPos = x;
             for (int y = x + 1; y < arr2.Length; y++)
             {
                if (arr2[y] < arr2[minPos])
                   minPos = y;
             }
    
             if (x != minPos && minPos < arr2.Length)
             {
                int temp = arr2[minPos];
                arr2[minPos] = arr2[x];
                arr2[x] = temp;
             }
          }
    
       }
    }
    

    基准代码

    [SimpleJob(RuntimeMoniker.Net50)]
    [MemoryDiagnoser()]
    public class SortBenchmark
    {
    
       private int[] data;
    
       [Params(100, 1000)]
       public int N;
    
       [GlobalSetup]
       public void Setup()
       {
          var r = new Random(42);
    
          data = Enumerable
             .Repeat(0, N)
             .Select(i => r.Next(0, N))
             .ToArray();
       }
    
       [Benchmark]
       public void Bubble() => Sort.BubbleSort(data.ToArray());
    
       [Benchmark]
       public void Selection() => Sort.SelectionSort(data.ToArray());
    }
    

    用法

    static void Main(string[] args)
    {
       BenchmarkRunner.Run<SortBenchmark>();
    }
    

    结果

    Method N Mean Error StdDev
    Bubble 100 8.553 us 0.0753 us 0.0704 us
    Selection 100 4.757 us 0.0247 us 0.0231 us
    Bubble 1000 657.760 us 7.2581 us 6.7893 us
    Selection 1000 300.395 us 2.3302 us 2.1796 us

    总结

    我们学到了什么?你的冒泡排序代码比较慢¯\_(ツ)_/¯

    【讨论】:

    • 我认为的最佳答案。非常有用的场景,考虑到开发人员通常不知道 GC/内存/JIT 和进程预热等所有这些方面如何影响性能并尝试编写自己的基准测试工具。
    • 需要明确的是,这些 N 非常小,因此很难进行公平比较。请注意,在 N=100 时,冒泡排序的速度提高了约 1000 倍。在 N=1000 时,只有 ~250x。我很好奇 N=10e5 或 10e6 会是什么。
    • @NateDiamond arg,选择排序从原始 OP 代码中有 1000 次循环迭代:/ ill update
    猜你喜欢
    • 2019-02-26
    • 2018-03-28
    • 2017-03-18
    • 2021-05-13
    • 2011-11-30
    • 2015-09-13
    • 2015-09-12
    • 2011-12-03
    • 2015-02-14
    相关资源
    最近更新 更多