|
楼主 |
发表于 2012-12-23 13:28
|
显示全部楼层
本帖最后由 lee1892 于 2012-12-27 11:24 编辑
也谈排序
论坛里常有关于各种排序法的讨论,从最基础的冒泡(Bubble Sort)、插入(Insertion Sort),到快速排序(Quick Sort)、希尔排序(Shell Sort)、堆排序(Heap Sort)等等。
我个人的看法是,讨论某种排序算法最快其实没什么意义,选择合适的算法才是我们应该考量的。
实际上,排序算法根据其特点可以分为如下几类:
交换排序:冒泡、快速 等
插入排序:插入、希尔、树排序 等
选择排序:堆排序 等
归并排序:归并排序(Merge Sort)等
分配排序:桶排序(Bucket Sort)、基数排序(Radix Sort)等
...
为论坛内所推崇的希尔排序、快速排序在很多时候并不是最佳的选择,比如一个整数数组、一个字符串数组、多关键字的二维数组等等。通常情况下,分配排序的时间效率会远远高于其他排序方法,但带来的往往是会消耗更多的空间(内存)。用空间换时间不仅仅是在战场上有用,同样适合于我们的计算机世界,呵呵。
下面这段代码测试了对一个正整数数组的排序速度,可以看到桶排序的方式完胜所有其他方法,而基数排序当把基数设置的偏大的时候(如对于10万内的数,使用1万作为基数)也有不错的表现,而基于比较后采用交换、插入的原地排序方法,则不可避免的会使用大量的循环。实际上由于这里的快速排序采用的是递归的方法,其使用的编译器生成的堆栈空间也是相当可观的。
另:下面的这个所谓桶排序,实际上是计数排序(Counting Sort)作为桶排序的特例演示,即使用待排序的数组的值作为桶数组的下标。
注:希尔排序的代码来自于 法师 的帖子 [原创] 有史以来最快的希尔排序 - 比历史贴快10倍,比Excel排序更快 - 兼论堆排序和快速排序
[code=vb]
Sub SpeedTest()
Dim arr&(), aData, i&, nLen&, nMax&, t#
nLen = 10 ^ 6: nMax = 10 ^ 5
ReDim arr(1 To nLen)
Randomize
For i = 1 To nLen
arr(i) = CLng(Rnd * nMax)
Next
aData = arr: t = Timer
Call RadixSort(aData)
Debug.Print "基数排序用时:" & Round(Timer - t, 2) & " 秒"
aData = arr: t = Timer
Call BucketSort(aData)
Debug.Print "桶排序用时:" & Round(Timer - t, 2) & " 秒"
aData = arr: t = Timer
Call ShellSort(aData, 1, nLen)
Debug.Print "希尔排序用时:" & Round(Timer - t, 2) & " 秒"
aData = arr: t = Timer
Call QuickSort(aData, 1, nLen)
Debug.Print "快速排序用时:" & Round(Timer - t, 2) & " 秒"
End Sub
Sub BucketSort(aData)
Dim i&, j&, aBucket&(), nMinNum&, nMaxNum&, n&
nMinNum = aData(LBound(aData)): nMaxNum = nMinNum
For i = LBound(aData) To UBound(aData)
If aData(i) < nMinNum Then nMinNum = aData(i)
If aData(i) > nMaxNum Then nMaxNum = aData(i)
Next
ReDim aBucket(nMinNum To nMaxNum)
For i = LBound(aData) To UBound(aData)
aBucket(aData(i)) = aBucket(aData(i)) + 1
Next
n = LBound(aData) - 1
For i = nMinNum To nMaxNum
If aBucket(i) > 0 Then
For j = 1 To aBucket(i)
n = n + 1
aData(n) = i
Next
End If
Next
End Sub
Sub RadixSort(aData)
Dim i&, aBucket&(), arr, nMaxNum&, nExp&, k%, nLB&, nUB&, nRadix&
nLB = LBound(aData): nUB = UBound(aData)
For i = nLB To nUB
If aData(i) > nMaxNum Then nMaxNum = aData(i)
Next
ReDim arr(nLB To nUB)
nExp = 1: nRadix = 10000
Do
ReDim aBucket(0 To nRadix - 1)
For i = nLB To nUB
k = Int(aData(i) / nExp) Mod nRadix
aBucket(k) = aBucket(k) + 1
Next
For i = 1 To nRadix - 1
aBucket(i) = aBucket(i) + aBucket(i - 1)
Next
For i = nUB To nLB Step -1
k = Int(aData(i) / nExp) Mod nRadix
arr(aBucket(k) + nLB - 1) = aData(i)
aBucket(k) = aBucket(k) - 1
Next
aData = arr
nExp = nExp * nRadix
If nMaxNum / nExp < 1 Then Exit Do
Loop
End Sub
Sub ShellSort(ArrKey, L As Long, R As Long)
Dim i As Long, j As Long, k As Long, h As Long, max_h As Long, offset As Long, one As Long
Dim Insert, h_arr() As Long, temp_h, temp_h2
temp_h = Array(1, 5, 19, 41, 109, 209, 505, 929, 2161, 3905, 8929, 16001, 36289, 64769, 146305, 260609, 587521, 1045055, 2354689, 4188161, 9427969)
'此增量序列也是拥有 O(N^1.25)的阶,但是明显比 h(n+1) = 3 * h(n) + 1更高效
temp_h2 = Array(1, 5, 19, 41, 109, 211, 503, 929, 2161, 3907, 8929, 16001, 36293, 64763, 146309, 260609, 587527, 1045055, 2354689, 4188161, 9427969)
ReDim h_arr(LBound(temp_h2) To UBound(temp_h2))
h_arr(LBound(h_arr)) = 1
For i = LBound(h_arr) + 1 To UBound(h_arr)
' h_arr(i) = 2.25 * h_arr(i - 1) + 1 '此增量序列拥有 O(N^1.25)的阶,但是速度也略为不如上面的序列1,5,19,41
' h_arr(i) = 3 * h_arr(i - 1) + 1 '此增量序列拥有 O(N^1.25)的阶,但是速度明显不如上面的2.25序列
h_arr(i) = temp_h2(i)
If h_arr(i) < (R - L) / 9 Then max_h = i
' If h_arr(i) > 2 ^ 31 / 2.25 Then Exit For
Next i
If max_h < LBound(h_arr) Then max_h = LBound(h_arr)
one = 1
For i = max_h To LBound(h_arr) Step -one
h = h_arr(i)
For offset = 0 To h - 1
For j = L + offset To R Step h
Insert = ArrKey(j)
For k = j - h To L + offset Step -h
If Insert < ArrKey(k) Then
ArrKey(k + h) = ArrKey(k)
ArrKey(k) = Insert
Else
Exit For
End If
Next k
Next j
Next offset
Next i
End Sub
Sub QuickSort(aData, nLeft&, nRight&)
Dim i&, j&, gKey, gTemp
If nLeft >= nRight Then Exit Sub
gKey = aData(nLeft)
i = nLeft + 1: j = nRight
Do
Do While i <= nRight
If aData(i) > gKey Then Exit Do
i = i + 1
Loop
Do While j > nLeft
If aData(j) < gKey Then Exit Do
j = j - 1
Loop
If i >= j Then Exit Do
gTemp = aData(i): aData(i) = aData(j): aData(j) = gTemp
Loop
gTemp = aData(nLeft): aData(nLeft) = aData(j): aData(j) = gTemp
Call QuickSort(aData, nLeft, j)
Call QuickSort(aData, j + 1, nRight)
End Sub
[/code]
有机会再聊聊排序的那点事,看看是不是有更高效的多关键字的二维数组排序之类的。
|
|