【内省式排序】能不能让sort排序在某种情况下更快呢?阅读sort源码后有感...

黑历史三

花了三四天的时间去搞排序,一开始的时候只是想写快排,然而和别人对比很慢,与sort对比更慢...于是心生不愤,开始研究sort

然后看着别人对sort源代码的分析(http://www.udpwork.com/item/12284.html),写了一个出来,嗯,性能和sort差不多了

然后问题又来了,在某种情况下,sort只需0.X秒,我那渣渣代码居然要30+秒,根本不是一个数量级....

然后又花了一天重写.....

结果:

1,阅读源码后,个人感觉就理论来说,sort已经是最接近nlogn底线的排序算法了,几乎没有提升的空间,即使有,也不会是我能想出来的。

2,于是,我想,如果转换成数组或指针排序,能不能快那么一点呢。

3,于是,我重写代码,用数组,指针代替迭代器,并且减少函数调用。

4,还是有一点成果的的,倒序数排序增幅有80%~120%(不知道为什么会这样),随机数排序有10%左右增幅。

5,主要是为了练习,有错误或改进请指出。


一个小小的sort排序,有很多很多的技巧,集合了堆排,快排,插入排序三种排序,不但加快了排序速度,而且在任何情况下复杂度都能接近nlogn。


首先,加入了递归深度限制,这个其实就是分治法递归树的高度,当超过这个限制时调用堆排,使快排无法恶化下去。

这个递归深度的值由排序规模N决定

inline int __lg(int n)
{
	int k;
	for (k = 0; n > 1; n >>= 1)
		++k;
	return k;
}
所以在排序前需要调用以上的函数,

首先,我们输入一个数组和数组规模

void QuickSort(int *a, int n)
{
	Quick_Sort(a, 0, n, __lg(n) * 2);
}

快排的枢纽采用三点取中值法

inline int MidValue(int &f, int &m, int &l)
{
	if (f > m)
	{
		if (m > l)  return m;
		else  return(f > l ? l : f);
	}
	else
	{
		if (f > l)  return f;
		else  return(l > m ? m : l);
	}
}

分割采用了双端扫描,无需顾虑边界问题。

int Patition(int *a, int first, int last, int pivot)
{
	while (true)
	{
		while (a[first] < pivot)  ++first;
		--last;
		while (a[last] > pivot)  --last;
		if (!(first < last))  return first;
		int e = a[first]; a[first] = a[last]; a[last] = e;
		++first;
	}
}

然后用数值16(谁知道他们经过什么测验得出的数字.....)决定快排底线,当元素低于16就采用插排。

const int threshold = 16;
void Quick_Sort(int *a, int first, int last, int depth_limit)
{
	while (first + threshold < last)
	{
		if (depth_limit == 0)
		{
			partial_sort(&a[first], &a[last], &a[last]);
			return;
		}
		--depth_limit;
		int cut = Patition(a, first, last,
						   MidValue(a[first], a[(first + last) / 2], a[last - 1]));
		Quick_Sort(a, cut, last, depth_limit);
		last = cut;//这个其实就是尾递归....
	}
//以下是插排
	if (first == 0)
	{
		int min = 0;

		for (int i = 1; i < last; ++i)
			min = a[i] < a[min] ? i : min;

		swap(a[0], a[min]);
	}

	for (int i = first + 1; i < last; ++i)
		Unguarded_Iinear_Insert(&a[i], a[i]);
}
以上代码中, partial_sort就是自带堆排,令我感到奇怪的是,我手写堆排无论怎么优化都比它慢.....

有趣的是,插排采用了unguarded思想,不用考虑边界问题,减少比较次数,加快了速度。具体请看(http://www.udpwork.com/item/12284.html)

首先,我们的快排把元素分为若干个小于或等于16的小区间,而且每个小区间递增

所以我们用插排排序小区间时,有两种情况,

1,普通的小区间,每一个元素都比上一个小区间的任意一个元素大,所以我们根本不用考虑边界问题,直接对比插入。

2,最左边(第一个)小区间,这个比较特殊,因为它没有上一个区间,所以我们需要找出这个小区间里的最小值,与第一个元素a[0]调换,然后就能不顾虑边界插排。

Unguarded_Iinear_Insert实现代码

void Unguarded_Iinear_Insert(int *last, int value)
{
	int *next = last;
	--next;

	while (value < *next)
	{
		*last = *next; last = next; --next;
	}
	*last = value;
}

(end)

接下来就是与sort战个痛快:

首先是5000万个随机int型数排序:

先上手写排序

平均用了3.3秒,不错不错

然后上我们的sort

平均3.6秒,看来用指针代替迭代器后,虽然可用面不广,但速度还是可以的。


然后是倒序数排序,手写居然快很多,原因不明...



语死早,写得不好不要打我。

(全文完)

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值