哈里在最近的编程生活中,突发奇想,开始着手文本的分析。
期间涉及到了数组的排序。于是想也没想祭出了自己祖传的“扑克牌排序法”,即插值排序。
不过当后期数组元素个数大于100万的时候,祖传方法明显慢了下来,需要长达几十分钟的排序(VB.6.0与VB.net均如此,VB6.0更慢)。
在排序算法的优化中,想到了一种新型排序方法:预留排序法。(新算法100万条数据仅需29毫秒,名字起的比较随意)
预留排序法的步骤主要有三步:
1、找到需要排序数组的最小值与最大值。(如果语言不支持负数组,则利用一个偏移将负值偏移至0)
2、获取需要排序数组的每种出现过的值的重复数量。
3、通过内存位置预测,将需要排序的数组的遍历序放入排序索引数组完成排序。
(以下例子默认需要排序的最小值大于或等于1)
VB6.0实例如下:(百万条数据3.6秒)
Public Function 预留法排序(数组() As Long, 排序() As Long)
Dim 数量() As Long, i As Long, 数组最大值 As Long, 偏移() As Long
ReDim 排序(UBound(数组))
For i = 1 To UBound(数组)
If 数组(i) > 数组最大值 Then
数组最大值 = 数组(i)
End If
Next
ReDim 数量(数组最大值), 偏移(数组最大值)
For i = 1 To UBound(数组)
数量(数组(i)) = 数量(数组(i)) + 1
Next
For i = 1 To UBound(数组)
偏移(数组(i)) = 偏移(数组(i)) + 1
排序(数组求和(数量, 数组(i)) + 偏移(数组(i))) = i
Next
End Function
Private Function 数组求和(数组() As Long, 截止序 As Long) As Long
Dim i As Long
For i = LBound(数组) To 截止序 - 1
数组求和 = 数组求和 + 数组(i)
Next
End Function
不得不说VB.NET的运算速度比VB6.0快得多得多,所以VB.net的实例哈里也准备了一份:(百万条数据29毫秒)
Public Sub 预留法排序(数组() As Long, 排序() As Long)
Dim 数量() As Long, i As Long, 数组最大值 As Long, 偏移() As Long
ReDim 排序(UBound(数组))
'获得数组最大值
For i = 1 To UBound(数组)
If 数组(i) > 数组最大值 Then
数组最大值 = 数组(i)
End If
Next
ReDim 数量(数组最大值), 偏移(数组最大值)
'重复值划分统计
For i = 1 To UBound(数组)
数量(数组(i)) += 1
Next
'最终排序
For i = 1 To UBound(数组)
偏移(数组(i)) += 1
排序(数组求和(数量, 数组(i)) + 偏移(数组(i))) = i
Next
End Function
Private Function 数组求和(数组() As Long, 截止序 As Long) As Long
Dim i As Long, 输出 As Long
For i = LBound(数组) To 截止序 - 1
输出 += 数组(i)
Next
Return 输出
End Function
(PS:以上所有耗时数据基于Intel i9 9900K默频。)