注册 登录
编程论坛 VB6论坛

求助!!请问对一维数组排序报溢出堆栈空间错误怎么修改?

ictest 发布于 2023-02-18 21:39, 2252 次点击
我想对数组内的数据按大小进行排序,大约20万条,用了两种方法,都会报:“实时错误 28  ,溢出堆栈空间”错误,我不断减少数据数量,15万条,10万条,5万条,大约在5万条左右就可以正常排序,请问,如何对程序修改,才能使其在20万条左右不会发生错误?

排序方法1(这个排序速度较快):

程序代码:
Private Sub Command1_Click()
    Call qsort(sj, 1, max_num) '对数组使用二分法
End Sub

Sub qsort(px() As Double, ByVal kaishi As Long, ByVal jieshu As Long)
    Dim temp As Double, j As Long, i As Long
    i = kaishi: j = jieshu '将i、j作为指针,从两侧向中部移动
    If kaishi < jieshu Then '控制是否进入循环
        temp = px(kaishi) '将数组第一个值赋给temp,暂时充当对比量
        While i < j
            Do While i < j '指针j从右向左移动,当遇到比temp小的数时,将该值移动到指针i的位置,并使i向右移动一位
                If px(j) <= temp Then px(i) = px(j): i = i + 1: Exit Do
                j = j - 1
            Loop
            Do While i < j '指针i从左向右移动,当遇到比temp大的数时,将该值移动到指针j的位置,并使j向左移动一位
                If px(i) > temp Then px(j) = px(i): j = j - 1: Exit Do
                i = i + 1
            Loop
        Wend
        px(i) = temp
        Call qsort(px(), kaishi, i - 1) '递归二分法过程进行排序
        Call qsort(px(), i + 1, jieshu)
    Else
        Exit Sub
    End If
End Sub



第二种方法(排序速度较慢):

程序代码:
Private Sub Command1_Click()
            l = max_num: S = 1: ReDim Num(l)
        For px_i = S To l
            Num(px_i) = sj(px_i)
        Next px_i
        Call QUICK_SORT(S, l, Num)
End Sub

'快速排序程序===========================================
Private Sub Exchange(ByRef n1 As Double, ByRef n2 As Double)
    Dim t As Double
    t = n1: n1 = n2: n2 = t
End Sub
Private Function PARTITION(ByVal p As Long, ByVal r As Long, ByRef a() As Double) As Long
    Dim X As Double, t As Long, i As Long, j As Long
    Randomize
    t = CLng((r - p) * Rnd + p)
    Call Exchange(a(r), a(t))
    X = a(r): i = p - 1
    For j = p To r - 1
        If a(j) <= X Then i = i + 1: Call Exchange(a(i), a(j))
    Next j
    Call Exchange(a(i + 1), a(r))
    PARTITION = i + 1
End Function
Private Sub QUICK_SORT(ByVal p As Long, ByVal r As Long, ByRef a() As Double)
    If p < r Then
        Dim q As Long
        q = PARTITION(p, r, a)
        Call QUICK_SORT(p, q - 1, a)
        Call QUICK_SORT(q + 1, r, a)
    End If
End Sub


请版主大人和过往高手大人不吝赐教!感谢!
13 回复
#2
冬瓜汤2023-02-19 02:00
Option Explicit

Private Declare Sub qsort CDecl Lib "msvcrt" ( _
                         ByRef pFirst As Any, _
                         ByVal lNumber As Long, _
                         ByVal lSize As Long, _
                         ByVal pfnComparator As Long)
               
Sub Main()
    Dim z() As Long
    Dim i As Long
    Dim s As String
   
    ReDim z(200000)
    Randomize
    For i = 0 To UBound(z)
        z(i) = Int(Rnd * 1000000)
    Next
   
    qsort z(0), UBound(z) + 1, LenB(z(0)), AddressOf Comparator
   
    Debug.Print "20万排序计时:" & GetTickCount() - t1 & "毫秒"
    For i = 1999991 To UBound(z)   '为节省时间,二十万个数据取最后10个数据看一下
        Debug.Print z(i)
    Next

End Sub

Private Function Comparator CDecl( _
                 ByRef a As Long, _
                 ByRef b As Long) As Long
    Comparator = a - b
End Function
只有本站会员才能查看附件,请 登录


VBCdeclFixDll插件https://wwi.

[此贴子已经被作者于2023-2-19 12:44编辑过]

#3
ictest2023-02-19 08:22
回复 2楼 冬瓜汤
大神,您的源码我无法使用,标红。如下图:
只有本站会员才能查看附件,请 登录

我查了一下,cdecl库是c函数库,vb6要使用的话,需要打cdecl补丁,我也下载了,如下:
只有本站会员才能查看附件,请 登录

但这个补丁我不会打,请教如何打这个补丁?
#4
wmf20142023-02-19 09:00
递归层次过深爆栈了。
可先用桶排,再对每个桶用你自己的快排,如果数据均匀的话,20万条数据,使用1000个桶,每个桶平均只有200个数据,再递归就不会爆栈了(其实这时用插入排序或希尔排序效率较高,不需要递归)。速度会慢一点。
#5
ictest2023-02-19 09:49
麻烦提供一下实例或者在我提供的源码里修改,两个排序方法也是根据在网上找到的实例修改的,我对数组排序不是很会😓
#6
冬瓜汤2023-02-19 12:50
回复 5楼 ictest
让我有点吃惊。我在上面2楼的回复 重贴一下 纯activeX dll下载链接,并附上 注册控件的 bat(路径自己填写,以管理员权限运行)。
#7
ictest2023-02-19 18:36
回复 6楼 冬瓜汤
真是对不起,您在二楼回复的DLL文件我没有看到,导致我重新在网上寻找,也不知道找到的是什么看不懂的东东,真是对不起!

您提供的DLL文件我已经加载成功,如下图:
只有本站会员才能查看附件,请 登录


但是,您提供的源码我怎么也运行不到您回复的结果图片那样的,我试了两种方式,都不行,都报错:
方式1:
只有本站会员才能查看附件,请 登录

结果是:
只有本站会员才能查看附件,请 登录



方式2:
只有本站会员才能查看附件,请 登录

结果是:
只有本站会员才能查看附件,请 登录


真是不好意思,还能不吝指教指教吗?谢谢您!
#8
冬瓜汤2023-02-19 19:02
因为是vb6的插件。所以你要在 外接程序管理器中启动它
只有本站会员才能查看附件,请 登录
#9
ictest2023-02-19 20:03
回复 8楼 冬瓜汤
我加载了啊。
只有本站会员才能查看附件,请 登录


您能把您的源码在VB6里做好保存一下发给我试一下吗?

另外,我一直没搞懂您的源码里没有Command1_Click()怎么运行的呢?又不是Private Sub Form_Load()加载窗体后直接运行..........
#10
冬瓜汤2023-02-20 14:17
我用你的 方式一 例子,重新调整一下 ,可以运行。
调用 cdecl协议 的函数,只能放在 模块中,不能放在 窗体或 类中声明。原因,以后你用多了,慢慢就会明白了。
只有本站会员才能查看附件,请 登录

只有本站会员才能查看附件,请 登录


[此贴子已经被作者于2023-2-20 14:19编辑过]

#11
ictest2023-02-20 23:07
回复 10楼 冬瓜汤
前辈您好,我将您的程序带入我的数据中测试,大约近20万数据,的确不溢出了,谢谢!但是有个问题还需要请教:
您的程序针对数据类型为long时,排序是正确的,但如果数据类型是doubles时,排序仅对整数部分有效,对小数部分无效(我把数组、a、b三处的数据类型修改成doubles了),结果如下图:
只有本站会员才能查看附件,请 登录


您帮忙看一下,我程序里哪里写错了或者哪里还需要做出修改的,麻烦请告之,谢谢。

附,我将您的程序带入我的数据中测试的源码:
只有本站会员才能查看附件,请 登录
#12
ictest2023-02-21 10:23
回复 10楼 冬瓜汤
前辈您好,我更换了一种排序方式,比您的方法速度慢了一些,但大约20万的数据也不会报溢出了,Double数据格式排序顺序也是正确了,现向您汇报一下,谢谢您的孜孜不倦的教导,让我对VB的编程技巧有了更深入的了解,谢谢您。
附,修改过的程序。
只有本站会员才能查看附件,请 登录

只有本站会员才能查看附件,请 登录


[此贴子已经被作者于2023-2-21 10:25编辑过]

#13
冬瓜汤2023-02-21 10:29
@11楼
你说的是对的。quicksort() 对浮点数或currency 排序,不能正确排序。关键问题是出在 comparator上面。改成下面的代码:
Public Function Comparator CDecl(ByRef a _
                 As Double, ByRef b _
                 As Double) As Long
    Comparator = IIf((a - b) > 0, 1, -1)
End Function
注意:对于浮点数或currency,用IIF(),返回 1或-1 这样的long值,只用1,-1这两个值!
虽然会影响性能,如20万long排序 182毫秒左右,20万double或currency排序是359毫秒左右,但还是在可承受范围。
只有本站会员才能查看附件,请 登录


[此贴子已经被作者于2023-2-21 10:32编辑过]

#14
ictest2023-02-21 17:01
回复 13楼 冬瓜汤
前辈您好,还有一个问题想请教一下:
您可以看到我提供的数据CSV里有很多参数,除去b(19)列外,我还需要对我还要分别对b(18)列,b(22)列,b(15)列,b(13)列,b(11)列分别读到数组a2_sj、a3_sj、a4_sj、a5_sj、a6_sj里,然后排序,然后输出到各个不同的文件里,重复调用语句:
qsort a1_sj(1), pass_num, LenB(a4_sj(1)), AddressOf Comparator

qsort a2_sj(1), pass_num, LenB(a4_sj(1)), AddressOf Comparator

qsort a3_sj(1), pass_num, LenB(a4_sj(1)), AddressOf Comparator

qsort a4_sj(1), pass_num, LenB(a4_sj(1)), AddressOf Comparator

qsort a5_sj(1), pass_num, LenB(a4_sj(1)), AddressOf Comparator

qsort a6_sj(1), pass_num, LenB(a4_sj(1)), AddressOf Comparator

发现排序越来越慢,然后会报错,这种情况怎么解决?

附:新修改的程序。
只有本站会员才能查看附件,请 登录
1