C#快速排序太慢
我现在正在学习不同类型的排序,我发现,从某一点开始,我的快速排序算法根本没有那么快 这是我的密码:C#快速排序太慢,c#,performance,quicksort,C#,Performance,Quicksort,我现在正在学习不同类型的排序,我发现,从某一点开始,我的快速排序算法根本没有那么快 这是我的密码: class QuickSort { // partitioning array on the key so that the left part is <=key, right part > key private int Partition(int[] arr, int start, int end) {
class QuickSort
{
// partitioning array on the key so that the left part is <=key, right part > key
private int Partition(int[] arr, int start, int end)
{
int key = arr[end];
int i = start - 1;
for (int j = start; j < end; j++)
{
if (arr[j] <= key) Swap(ref arr[++i], ref arr[j]);
}
Swap(ref arr[++i], ref arr[end]);
return i;
}
// sorting
public void QuickSorting(int[] arr, int start, int end)
{
if (start < end)
{
int key = Partition(arr, start, end);
QuickSorting(arr, start, key - 1);
QuickSorting(arr, key + 1, end);
}
}
}
class Test
{
static void Main(string[] args)
{
QuickSort quick = new QuickSort();
Random rnd = new Random(DateTime.Now.Millisecond);
int[] array = new int[1000000];
for (int i = 0; i < 1000000; i++)
{
int i_rnd = rnd.Next(1, 1000);
array[i] = i_rnd;
}
quick.QuickSorting(array, 0, array.Length - 1);
}
}
类快速排序
{
//在键上对数组进行分区,使左侧部分为键
私有int分区(int[]arr,int start,int end)
{
int key=arr[end];
int i=开始-1;
对于(int j=开始;j<结束;j++)
{
如果(arr[j]你的排序速度有多快以及你应该使用哪种算法取决于你的大量输入数据。它是随机的、近似排序的、反向的等等
有一个非常好的页面,说明了不同的排序算法是如何工作的:
您考虑过将
交换方法内联吗?这样做应该不难,但可能是JIT发现内联很难
当我根本没有看到这个问题时,您可能想尝试我的代码(可能是最简单的递归形式),看看它对您的性能有何影响。如果它做得好,请尝试找出差异所在
虽然不同的算法对同一数据的行为会有所不同,但我不希望在随机生成的数据上看到如此大的差异。您有1000000个随机元素和1000个不同的值。因此,我们可以预期大多数值在数组中出现大约1000次。这会给您一些二次O(n^2)运行时间
将数组划分为1000个部分(每个分区包含相同的编号)时,堆栈深度约为log2(1000),约为10。(假设对分区的调用将其整齐地划分为两个部分。)这大约是10000000个操作
要对最后1000个分区进行快速排序,所有分区都包含1000个相同的值。我们需要1000次1000+999+998+…+1比较。(在每一轮快速排序中,只需删除键/轴,即可将问题减少一次。)这就产生了500000000个操作。对1000个分区进行快速排序的最理想方法是1000乘以1000*10个操作=10000000。由于这些值相同,所以这里出现了二次情况,即快速排序的最坏情况性能。因此,大约在快速排序的中途,它会出现最坏情况行为
如果每个值只出现几次,那么在O(N^2)
或O(N logN)
中对这些小分区进行排序并不重要。但是在这里,我们有很多巨大的分区需要在O(N^2)
中进行排序
要改进代码:分为3个部分。小于轴、等于轴、大于轴。然后,只对第一个和最后一个分区进行快速排序。您需要进行额外的比较;首先测试相等性。但我认为,对于此输入,速度会快得多。是否将int装箱以便它可以引用?这是almost当然是交换。@Rup:不,它不会装箱整数。谢谢,我会尝试你的代码。交换部分如下:私有无效交换(ref int a,ref int b){int temp=a;a=b;b=temp;}您的代码运行速度快了5倍,但我仍在寻找原因。与此同时,正如Lasse V.Karlsen在上文中建议的那样,我将随机数的数量从1改为1000000(而不是1改为1000),这真的很有帮助。数据是随机的,我甚至尝试了在排序时选择关键元素的随机版本,它没有做任何更改。感谢链接!尝试将数值的范围从1000增加到10000000,我认为这里的问题是,您将值混洗了太多次。有趣的是,您完全正确!我将数字增加到1000000,现在快速排序非常快。谢谢。O(1/n)算法是一个永动机装置。你使用了你能选择的最差的轴。由于接近二次的行为,在较小的范围内你会得到更差的结果。如果你使所有元素都相同,那么你将真正得到二次的行为。看看Bentley McIlroy分区来处理重复的元素。@Hans:这只是一个糟糕的p如果数据不像OP代码中那样随机化,则选择ivot。