文号
t30701
阅读
11637
回复
15
收藏
5
比快排还快的排序
2010/12/12
IP:浙江
这种排序叫倍增排序,用于对正整数排序,负整数的话可以先都加上一个大的正数再用这种排序,它是基于寻址来排序的,不像快排是基于比较的,倍增排序的速度比快速排序快,是稳定的,不会退化到O(n^2),其时间复杂度为O(n+sqrt(m))(此处m指这些正整数的最大值,用dword类型时取2^32-1,n指待排序数的个数),空间复杂度也是一样,怎么样?是不是比O(n*Logn)快?它的原理和计数排序相似,但排序用的指标不同,它是用a[i]和sqrt(m)的余数、商作为指标,为了加快运行速度,用位运算计算商和余数。
一下是pascal源代码:
const
max=65535;//这边的max就相当于sqrt(m),max一定要(11....11)形式的二进制数,这样才可以用and来求余,提高速度。
var
i,n:longint;
a,b:array[1..1000000]of longint;
m,d:array[0..max+1]of longint;
begin
readln(n);
for i:=1 to n do
begin
read(a[ i]);
inc(m[1+a[ i] and max]);//此时的m表示余数a[ i] mod (max+1)为的数有几个,a[ i] and max=a[ i] mod (max+1)
inc(d[1+a[ i] shr 16]);//此时的d表示商为a[ i] div (max+1)的数有几个,a[ i] shr 16=a[ i] div (max+1)
end;
for i:=1 to max+1 do
begin
inc(m[ i],m[i-1]);//此时的m表示余数小于i(因为前面有加1,所以这里是小于)的数字的个数
inc(d[ i],d[i-1]);//此时的d表示商小于i(因为前面有加1,所以这里是小于)的数字的个数
end;
for i:=1 to n do//这边是按照余数排序
begin
inc(m[a[ i] and max]);//设t=a[ i]and max,这里表示余数为t的数应该放在的地址,因为前面累加之后,m[ t]已经表示余数小于t的数字有多少个,所以当有数字的余数为t时,应从t+1个位置开始放置在b数组,inc(m[ t])表示放置第几个余数为t的数字的地址。
b[m[a[ i] and max]]:=a[ i];
end;
for i:=1 to n do//这边基于已经按照余数排好序的数据再按照商排序
begin
inc(d[b[ i] shr 16]);//设t=a[ i]shr 16,这里表示商为t的数应该放在的地址,因为前面累加之后,d[t]已经表示商小于t的数字有多少个,所以当有数字的商为t时,应从t+1个位置开始放置在a数组,inc(m[ t])表示放置第几个商为t的数字的地址。
a[d[b[ i] shr 16]]:=b[ i];
end;
for i:=1 to n-1 do write(a[ i],' ');
write(a[n]);
end.
ltc作者
16年1个月前IP:未同步
268919
1楼
与其他排序的比较
快速排序 时间复杂度O(n*logn)         空间复杂度O(n*logn)
基数排序 时间复杂度O(n+m)            空间复杂度O(n+m)
倍增排序 时间复杂度O(n+sqrt(m))   空间复杂度O(n+sqrt(m))
虽然m是常数,但是在大多数情况下m较大,故这样写
引用
评论
ltc作者
16年1个月前IP:未同步
268922
2楼
为什么a [ i ]会变成a
引用
评论
ltc作者
16年1个月前IP:未同步
268923
3楼
由于空格会变成?
不保留缩进了
引用
评论
ltc作者
16年1个月前IP:未同步
268924
4楼
实测结果下次再发
引用
评论
ltc作者
16年1个月前IP:未同步
269348
5楼
没人给个评价?
引用
评论
16年1个月前IP:未同步
269570
6楼
Python中的sort()函数用的是timsort,据说有超自然的能力,复杂度远低于O(n* log n),但是晚上资料太少,只有wiki中有段很短的介绍 XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
引用
评论
ltc作者
16年1个月前IP:未同步
269746
7楼
上快排和倍增排序的实测结果
用清澄测的
1-5个点   n=1000
6-10个点  n=10000
11-15个点 n=100000
16-20个点 n=1000000
21-25个点 n=10000000
数据为longint类型随机数据
引用
评论
ltc作者
16年1个月前IP:未同步
269752
8楼
1.jpg
2.jpg
引用
评论
16年1个月前IP:未同步
269765
9楼
身为一个OIer……我确实想说点什么……

首先……这不是排序算法而是统计算法吧……而且我觉得是杂交的基数排序和桶排序……对于排序算法可以证明最优复杂度O(nlogn),并且排序算法复杂度不受数字范围的影响。

其次……发现它比快排快只是因为你的数字范围太小了……试试字符串排序?!或者实数、高精?!!!

而且快排只是期望nlogn(由主定理可证),最优O(n),最坏还是n^2的,你可以找个严格的排序算法比较嘛……比如归并、堆排、Treap神马的……
引用
评论
16年1个月前IP:未同步
269767
10楼
还有,我查了一下,timsort好像就是归并排序的衍生产物,复杂度貌似和lg(n!)有关,还有说是n到nlogn的,确实也挺接近归并排序的,一点也不超自然……

真正超自然的排序应该是用量子计算机排序,直接并行产生全排列,并行check,留下正确值
引用
评论
ltc作者
16年1个月前IP:未同步
269787
11楼
在大多数情况下,你认为longint类型不够用?
很多排序的题目用-2^31+1~2^31-1完全够用
要用到高精度的,在OI中估计n不会太大吧(其实我也是OIer)
而且你把这个和基数排序或者计数排序比较一下,就会发现其中的优点
引用
评论
ltc作者
16年1个月前IP:未同步
269788
12楼
高精度的话比较不就要O(n)了???
引用
评论
16年1个月前IP:未同步
269966
13楼
很反感OIer的路过[s:252]
引用
评论
16年0个月前IP:未同步
271089
14楼
回 8楼(ltc) 的帖子
就这个空间复杂度和时间复杂度,还是快排好一些
引用
评论
15年11个月前IP:未同步
281553
15楼
引用第11楼ltc于2010-12-19 12:03发表的  :
在大多数情况下,你认为longint类型不够用?
很多排序的题目用-2^31+1~2^31-1完全够用
要用到高精度的,在OI中估计n不会太大吧(其实我也是OIer)
而且你把这个和基数排序或者计数排序比较一下,就会发现其中的优点

这个还算可以吧,long long型数据都出来了,尽管这个属于OI尽量少用或不用的数据类型
引用
评论
笔记
{{note.content}}
{{n.user.username}}
{{fromNow(n.toc)}} {{n.status === noteStatus.disabled ? "已屏蔽" : ""}} {{n.status === noteStatus.unknown ? "正在审核" : ""}} {{n.status === noteStatus.deleted ? '已删除' : ''}}
  • 编辑
  • 删除
  • {{n.status === 'disabled' ? "解除屏蔽" : "屏蔽" }}
我也是有底线的