ExcelHome技术论坛

 找回密码
 免费注册

QQ登录

只需一步,快速开始

快捷登录

搜索
EH技术汇-专业的职场技能充电站 妙哉!函数段子手趣味讲函数 Excel服务器-会Excel,做管理系统 Excel Home精品图文教程库
HR薪酬管理数字化实战 Excel 2021函数公式学习大典 Excel数据透视表实战秘技 打造核心竞争力的职场宝典
300集Office 2010微视频教程 数据工作者的案头书 免费直播课集锦 ExcelHome出品 - VBA代码宝免费下载
用ChatGPT与VBA一键搞定Excel WPS表格从入门到精通 Excel VBA经典代码实践指南
查看: 7431|回复: 15

[讨论] 快速排序代码比拼

[复制链接]

TA的精华主题

TA的得分主题

发表于 2015-5-5 00:31 | 显示全部楼层 |阅读模式
[广告] Excel易用宝 - 提升Excel的操作效率 · Excel / WPS表格插件       ★免费下载 ★       ★ 使用帮助
本帖已被收录到知识树中,索引项:排序
本帖最后由 loquat 于 2015-6-8 11:55 编辑

仅仅针对Long类型的数据来做排序算法的话,我目前见过的最快的算法如下。
欢迎也希望大侠能指点
这个排序仅仅对整数数组效率比较好,对字符串或者浮点数都没有较高效率
主要是swap在字符串和浮点数时,会大大降低效率,我的理解应该是这样

代码一是完全的API算法,调用msvcrt.dll里的qsort函数,在IDE界面下大部分情况下优势相当明显
代码二是纯VB算法,在VB编译后的情况下比代码一还要强大,且各种类型的数据表现都大体一致

另,发一个各种快排代码的比拼。。。 DualPivotQuickSortCountComparisons.zip (144.45 KB, 下载次数: 196) 附件是VB工程,工程里已经验证了排序结果,不需要再另外验证。
具体有问题请vbgood相关帖子,链接一个:ht【补丁】tp://ww【补丁】w.vbgood.c【补丁】om/thread-141406-1-1.h【补丁】tml

  1. Private Declare Function CallWindowProc Lib "user32.dll" Alias "CallWindowProcA" (ByVal lpPrevWndFunc As Long, ByVal hwnd As Long, ByVal msg As Long, ByVal wParam As Long, ByVal lParam As Long) As Long
  2. Private Declare Function LoadLibrary Lib "kernel32.dll" Alias "LoadLibraryA" (ByVal lpLibFileName As String) As Long
  3. Private Declare Function FreeLibrary Lib "kernel32.dll" (ByVal hLibModule As Long) As Long
  4. Private Declare Function GetProcAddress Lib "kernel32.dll" (ByVal hModule As Long, ByVal lpProcName As String) As Long

  5. Private m_bCode(42) As Byte, m_hMod As Long, m_lpFunc As Long

  6. Friend Sub QuickSort(idxArray() As Long, ByVal nStart As Long, ByVal nEnd As Long)
  7. If nEnd - nStart <= 1 Then Exit Sub
  8. If m_lpFunc Then
  9.     CallWindowProc VarPtr(m_bCode(0)), VarPtr(idxArray(nStart)), nEnd - nStart + 1, m_lpFunc, 0
  10.     Exit Sub
  11. End If
  12. End Sub

  13. Private Sub Class_Initialize()
  14. Dim s As String, m As Long, i As Long
  15. m_hMod = LoadLibrary("msvcrt.dll")
  16. m_lpFunc = GetProcAddress(m_hMod, "qsort")
  17. s = "89E0E800000000830424156A04FF7008" + _
  18.     "FF7004FF500C83C410C21000" + _
  19.     "8B4C24048B4424088B108B0129D0C3"
  20. m = Len(s) \ 2
  21. For i = 0 To m - 1
  22.     m_bCode(i) = CLng("&H" + Mid(s, i + i + 1, 2))
  23. Next i
  24. End Sub
  25. Private Sub Class_Terminate()
  26. FreeLibrary m_hMod
  27. End Sub
复制代码
  1. Public Sub QSort2(ByRef key_arr() As Long, L As Long, R As Long)
  2.     Dim i As Long, j As Long
  3.     Dim x As Long, Swap As Long
  4.     Const k As Long = 60
  5.     If R - L <= k Then
  6.         For i = L + 1 To R
  7.             x = key_arr(i)
  8.                
  9.             For j = i - 1 To L Step -1
  10.                 If key_arr(j) <= x Then Exit For
  11.                 key_arr(j + 1) = key_arr(j)
  12.             Next
  13.             key_arr(j + 1) = x
  14.         Next
  15.     Else
  16.         x = key_arr((L + R) \ 2)
  17.         i = L
  18.         j = R
  19.         Do While i <= j
  20.             Do While key_arr(i) < x
  21.                 i = i + 1
  22.             Loop
  23.             
  24.             Do While key_arr(j) > x
  25.                 j = j - 1
  26.             Loop
  27.            
  28.             If i <= j Then
  29.                 Swap = key_arr(i)
  30.                 key_arr(i) = key_arr(j)
  31.                 key_arr(j) = Swap
  32.                 i = i + 1
  33.                 j = j - 1
  34.             End If
  35.         Loop

  36.         '递归方法
  37.         If L < j Then
  38.             Do While key_arr(j) = x
  39.                 j = j - 1
  40.                 If j = L Then Exit Do
  41.             Loop
  42.             Call QSort2(key_arr, L, j)
  43.         End If
  44.         If i < R Then
  45.             Do While key_arr(i) = x
  46.                 i = i + 1
  47.                 If i = R Then Exit Do
  48.             Loop
  49.             Call QSort2(key_arr, i, R)
  50.         End If
  51.     End If
  52. End Sub
复制代码

TA的精华主题

TA的得分主题

 楼主| 发表于 2015-5-5 00:34 | 显示全部楼层
[广告] VBA代码宝 - VBA编程加强工具 · VBA代码随查随用  · 内置多项VBA编程加强工具       ★ 免费下载 ★      ★使用手册
对比不同算法时,采用如下代码测试比较
  1. Sub 正确性验证和效率对比()
  2. Dim n() As Long
  3. m = 1048576    '很多个随机数
  4. ReDim n(1 To m)
  5. For i = 1 To m
  6.     n(i) = 10000 * Rnd
  7. Next
  8. Dim a() As Long, b() As Long
  9. a = n: b = n    '保证不同算法使用同一个数组,以证明效率
  10. t = Timer
  11. QuickSort_1 a  '对数组a排序
  12. Debug.Print Timer - t
  13. iSum = 0
  14. For i = 1 To m - 1
  15.     iSum = iSum - (a(i) > a(i + 1))   '验证正确性
  16. Next
  17. Debug.Print iSum  '如果iSum不等于0,证明算法有误
  18. t = Timer
  19. QuickSort_2 b           '对数组b排序
  20. Debug.Print Timer - t
  21. iSum = 0
  22. For i = 1 To m - 1
  23.     iSum = iSum - (b(i) > b(i + 1))   '验证正确性
  24. Next
  25. Debug.Print iSum  '如果iSum不等于0,证明算法有误
  26. End Sub
复制代码

TA的精华主题

TA的得分主题

发表于 2015-5-5 00:38 | 显示全部楼层

TA的精华主题

TA的得分主题

发表于 2015-5-5 09:20 | 显示全部楼层

TA的精华主题

TA的得分主题

 楼主| 发表于 2015-5-5 12:36 | 显示全部楼层
[广告] Excel易用宝 - 提升Excel的操作效率 · Excel / WPS表格插件       ★免费下载 ★       ★ 使用帮助
liucqa 发表于 2015-5-5 09:20
可以用指针交换来快速交换字符串

我觉得也应该用指针,但是还没学会指针用法

TA的精华主题

TA的得分主题

 楼主| 发表于 2015-5-6 18:59 | 显示全部楼层
[广告] VBA代码宝 - VBA编程加强工具 · VBA代码随查随用  · 内置多项VBA编程加强工具       ★ 免费下载 ★      ★使用手册
CSDN上找到如下代码,但是直接移植过来好像效率没升反降
  1. 'VB标准方法:
  2. Sub SwapStrPtr(sA As String, sB As String)
  3.     Dim lTmp As Long
  4.     CopyMemory lTmp, ByVal VarPtr(sA), 4
  5.     CopyMemory ByVal VarPtr(sA), ByVal VarPtr(sB), 4
  6.     CopyMemory ByVal VarPtr(sB), lTmp, 4
  7. End Sub
  8. '用指针的方法:
  9. Sub SwapStrPtr2(sA As String, sB As String)
  10.     Dim lTmp As Long
  11.     Dim pTmp As Long, psA As Long, psB As Long
  12.     pTmp = VarPtr(lTmp): psA = VarPtr(sA): psB = VarPtr(sB)
  13.     CopyMemoryPtr pTmp, psA, 4
  14.     CopyMemoryPtr psA, psB, 4
  15.     CopyMemoryPtr psB, pTmp, 4
  16. End Sub

  17. '最快的方法
  18. Sub SwapStrPtr3(sA As String, sB As String)
  19.     Dim lTmp As Long
  20.     Dim pTmp As Long, psA As Long, psB As Long
  21.     pTmp = StrPtr(sA): psA = VarPtr(sA): psB = VarPtr(sB)
  22.     CopyMemory ByVal psA, ByVal psB, 4
  23.     CopyMemory ByVal psB, pTmp, 4
  24. End Sub
复制代码

TA的精华主题

TA的得分主题

发表于 2015-5-6 22:40 | 显示全部楼层
loquat 发表于 2015-5-5 12:36
我觉得也应该用指针,但是还没学会指针用法

用数组做索引。数组先指向各字符串。排序要交换时,就把数组中的值做交换。

TA的精华主题

TA的得分主题

 楼主| 发表于 2015-5-7 00:26 | 显示全部楼层
汇铁 发表于 2015-5-6 22:40
用数组做索引。数组先指向各字符串。排序要交换时,就把数组中的值做交换。

了然,但是我还不太懂指针,继续研究一下。。。

TA的精华主题

TA的得分主题

 楼主| 发表于 2015-5-12 22:07 | 显示全部楼层

TA的精华主题

TA的得分主题

 楼主| 发表于 2015-5-19 17:35 | 显示全部楼层
本帖最后由 loquat 于 2015-5-20 13:27 编辑

之前的全部推翻,这个才是最快的。。。
不过本函数其实还有优化的空间,那个for循环应该还可以用汇编做提高效率。
  1. Private Declare Function CallWindowProc Lib "user32.dll" Alias "CallWindowProcA" (ByVal lpPrevWndFunc As Long, ByVal hwnd As Long, ByVal msg As Long, ByVal wParam As Long, ByVal lParam As Long) As Long
  2. Private Declare Function LoadLibrary Lib "kernel32.dll" Alias "LoadLibraryA" (ByVal lpLibFileName As String) As Long
  3. Private Declare Function FreeLibrary Lib "kernel32.dll" (ByVal hLibModule As Long) As Long
  4. Private Declare Function GetProcAddress Lib "kernel32.dll" (ByVal hModule As Long, ByVal lpProcName As String) As Long

  5. Private m_bCode(42) As Byte, m_hMod As Long, m_lpFunc As Long

  6. Friend Sub QuickSort(idxArray() As Long, ByVal nStart As Long, ByVal nEnd As Long)
  7. If nEnd - nStart <= 1 Then Exit Sub
  8. If m_lpFunc Then
  9.     CallWindowProc VarPtr(m_bCode(0)), VarPtr(idxArray(nStart)), nEnd - nStart + 1, m_lpFunc, 0
  10.     Exit Sub
  11. End If
  12. End Sub

  13. Private Sub Class_Initialize()
  14. Dim s As String, m As Long, i As Long
  15. m_hMod = LoadLibrary("msvcrt.dll")
  16. m_lpFunc = GetProcAddress(m_hMod, "qsort")
  17. s = "89E0E800000000830424156A04FF7008" + _
  18.     "FF7004FF500C83C410C21000" + _
  19.     "8B4C24048B4424088B108B0129D0C3"
  20. m = Len(s) \ 2
  21. For i = 0 To m - 1
  22.     m_bCode(i) = Val("&H" + Mid(s, i + i + 1, 2))
  23. Next i
  24. End Sub

  25. Private Sub Class_Terminate()
  26. FreeLibrary m_hMod
  27. End Sub
复制代码

调用
Sub test2()
m = 1048576
ReDim n(1 To m) As Long
ReDim b(1 To m) As Long
For i = 1 To m
    n(i) = 10000 * Rnd
Next
t = Timer
Dim a As New ISort2
b = n
a.QuickSort b, 1, m, a
Debug.Print Timer - t
For i = 1 To m - 1
    iSum = iSum - (b(i) <= b(i + 1))
Next
Debug.Print iSum
End Sub
您需要登录后才可以回帖 登录 | 免费注册

本版积分规则

手机版|关于我们|联系我们|ExcelHome

GMT+8, 2024-4-27 00:20 , Processed in 0.041099 second(s), 12 queries , Gzip On, MemCache On.

Powered by Discuz! X3.4

© 1999-2023 Wooffice Inc.

沪公网安备 31011702000001号 沪ICP备11019229号-2

本论坛言论纯属发表者个人意见,任何违反国家相关法律的言论,本站将协助国家相关部门追究发言者责任!     本站特聘法律顾问:李志群律师

快速回复 返回顶部 返回列表