Bàn về thuật toán sort mảng

Liên hệ QC

Hoàng Trọng Nghĩa

Chuyên gia GPE
Thành viên BQT
Moderator
Tham gia
17/8/08
Bài viết
8,655
Được thích
16,712
Giới tính
Nam
Hổm rày chưa thấy đề tài nào sôi nổi, giờ thử đố một đề tài xem có tạo hứng thú cho mọi người không.

Nếu tôi có một chuỗi với các số (dương) lộn xộn:

Chuỗi = "1, 8, 6, 4, 5, 11, 12, 13, 16, 15, 20, 14, 10, 7"

Giờ, tôi sẽ viết hàm như thế nào để dãy số trên được lược lại, và có kết quả như dưới?

Kết quả = "1, 4-8, 10-16, 20"

Các bạn thử xem nào!
 
Lần chỉnh sửa cuối:
Em HIỂU hết những gì mọi người nói
Ẹc... Ẹc...
Có điều tại đồng chí kia nói hơi quá:

Ngớ ngẩn gì đâu chứ trong khi ta thấy nó KHÔNG VÒNG LẬP thì ta nói nó không lập thôi (bên trong ruốt nó lập hay không thây kệ nó)
Vấn đề là: Nếu ta tự viết được cái của riêng ta nó NHANH hơn thì ta xài, không thì cứ xài cái mà ta nghĩ là KHÔNG LẬP kia cho khỏe
Có gì đâu mà NGỚ với NGẨN nhỉ?
Cũng giống như dùng VBScript.RegEx, ta thấy nó không vòng lập và gọn thì ta xài ---> Ai rảnh đâu mà suy nghĩ bên trong ruột nó viết thế nào. Mà dù có cố suy nghĩ thì ta chắc gì đã biết bên trong nó có LẬP HAY KHÔNG LẬP mà bảo là NÓI SAI TỪ
----------------
Ngộ thiệt (tại ku Nghĩa bực mình thôi chứ em thì chẳng quan tâm)
Ẹc... Ẹc...


Khẳng định đó là sai từ, để lần sau chúng ta cùng không lặp lại ndu ah,

Vì nếu không lần sau lại đố tiếp lại nói KHÔNG DÙNG VÒNG LẶP thì làm nhiều người hiểu lầm sai bản chất vấn đề- và mất hết đi ý nghĩa thuật toán cơ bản (dù các công cụ kia vẫn sử dụng các thuật toán cơ bản mà thui - chúng ta cũng có thể từ thuật toán cơ bản mà xây dựng các công cụ tương tự chứ không phải cao siêu gì - đây cũng là cách hiểu sai về "đi tắt đón đầu" cuối cùng người ta lại vượt trước chúng ta vì họ đã có cái cơ bản nền tảng đê vượt và ta cứ trên mây trên ngọn)
 
Upvote 0
Khẳng định đó là sai từ, để lần sau chúng ta cùng không lặp lại ndu ah,

Vì nếu không lần sau lại đố tiếp lại nói KHÔNG DÙNG VÒNG LẶP thì làm nhiều người hiểu lầm sai bản chất vấn đề- và mất hết đi ý nghĩa thuật toán cơ bản (dù các công cụ kia vẫn sử dụng các thuật toán cơ bản mà thui - chúng ta cũng có thể từ thuật toán cơ bản mà xây dựng các công cụ tương tự chứ không phải cao siêu gì - đây cũng là cách hiểu sai về "đi tắt đón đầu" cuối cùng người ta lại vượt trước chúng ta vì họ đã có cái cơ bản nền tảng đê vượt và ta cứ trên mây trên ngọn)
Hãy suy nghĩ đơn giản chút đi bạn, theo tui nghĩ thì việc viết không dùng vòng lặp có nghĩa là mình không được viết vòng lặp, nhưng không cấm việc sử dụng những công cụ mà bên trong nó có vòng lặp hay là không. Ta chả cần quan tâm bên trong nó có gì cho mệt xác, chỉ cần biết cái nào hợp, hay cảm thấy hay là dùng.
 
Upvote 0
Khẳng định đó là sai từ, để lần sau chúng ta cùng không lặp lại ndu ah,

Vì nếu không lần sau lại đố tiếp lại nói KHÔNG DÙNG VÒNG LẶP thì làm nhiều người hiểu lầm sai bản chất vấn đề- và mất hết đi ý nghĩa thuật toán cơ bản (dù các công cụ kia vẫn sử dụng các thuật toán cơ bản mà thui - chúng ta cũng có thể từ thuật toán cơ bản mà xây dựng các công cụ tương tự chứ không phải cao siêu gì - đây cũng là cách hiểu sai về "đi tắt đón đầu" cuối cùng người ta lại vượt trước chúng ta vì họ đã có cái cơ bản nền tảng đê vượt và ta cứ trên mây trên ngọn)

Dạ vâng!
Bạn cho đó là SAI TỪ thì cứ thế đi (vì mình không biết thế nào là ĐÚNG cả, mình thấy nó không lập thì nói không lâp)
Đứng trên cương vị là THÀNH VIÊN thì mình cũng chẳng quan tâm mấy vấn đề này (thích cái nào xài cái đó)
Đứng trên cương vị là MOD thì mình thiết nghĩ cần phải nhắn nhủ đôi lời với bạn: Mai này dù có góp ý gì cũng cố lựa lời mà nói, tránh gây mất tình đoàn kết trên diễn đàn... Vì bạn và các thành viên khác trên diễn đàn này cũng chưa có mối quan hệ tốt đến mức có thể nói sao cũng được
Vậy đi nha!
Chúng ta tiếp tục ĐỐ VUI nhé
 
Upvote 0
Đứng trên cương vị là THÀNH VIÊN thì mình cũng chẳng quan tâm mấy vấn đề này (thích cái nào xài cái đó)
Đứng trên cương vị là MOD thì mình thiết nghĩ cần phải nhắn nhủ đôi lời với bạn: Mai này dù có góp ý gì cũng cố lựa lời mà nói, tránh gây mất tình đoàn kết trên diễn đàn... Vì bạn và các thành viên khác trên diễn đàn này cũng chưa có mối quan hệ tốt đến mức có thể nói sao cũng được

Chính vì dùng gì thì dùng --> thì mới cần đừng áp đặt quan niệm SAI của mình cho người khác,

Không phải không có lựa lời, mà đó là bài nói về khái niệm sai lầm (như là chắc chắn KHÔNG CÓ VÒNG LẶP trong các công cụ có sẵn) đó cần bỏ (nhấn mạnh để bỏ) --> không nói đến ai cả, thì sao là "mất tình đoàn kết" - trong chuyên môn cần thẳng thắn thừa nhận (khái niệm không đúng thì phải nói là không đúng), đừng tự ti như vậy

Cám ơn ndu đã nói vậy, và có thể hiểu là có quá nhiều nhạy cảm ở đây chăng (?)

Ở đây tôi thấy cách của huuthang_bd (selection sort) hay Quick sort của hungpecc1 (dĩ nhiên còn nhiều thuật toán sắp xếp khác - đó mới điều cần học cần nói mà không bị phủ nhận hoặc mờ đi bởi nhận định sai) là các cách cơ bản và ứng dụng tốt trong bài này - vì chẳng bao giờ có chuỗi nào tách mà dài lê văn thê đến hàng 10 000 thành phần tức là vài chục ngàn ký tự cả (nếu thế thì gọi là phi thực tế)

Chúc các bạn tiếp tục duy trì chủ đề đúng ý nghĩa đố vui của nó
 
Lần chỉnh sửa cuối:
Upvote 0
Xét về tốc độ, thì 2 thằng em này cũng ngang ngang nhau, nếu dùng For ... Next do mình xử lý chuỗi thì có mà chờ dài cổ, tôi đã thử code của huuthang_bd với 1,000 mục cho ra thời gian đâu có bao nhiêu: 114 giây! Trong khi đó, với 60,000 mục thì 2 thằng em trên xử lý trong vòng 2 giây.

Tôi thấy so sánh như thế là không ... , thôi cứ gọi là "không công bằng".

Bạn đố về việc sắp xếp nhưng không phải đố về code sắp xếp tốc độ nhanh mà là code sắp xếp để không ra 1,4,5,6,7,10 mà ra 1,4-7,10.

Như vậy người làm sẽ viết code chỉ để làm ra kết quả. Họ có thể chỉ viết code đơn giản (ngại?) mà không đặt trọng tâm vào việc viết code dùng để sort (không là nội dung của bài đố). Vì tôi tin rằng huuthang_bd biết nhiều kiểu sort chứ không phải kiểu đơn giản nhưng rất chậm như bạn dùng để so sánh.

Đã có lúc tôi đã gửi lên GPE code dùng để sort theo thuật toán quick sort.

http://www.giaiphapexcel.com/forum/...cho-sắp-xếp-mảng-nhờ-test-hộ-và-góp-ý

Cũng cần trả lời ndu về ý kiến

Trên diễn đàn mọi người vẫn mong có được 1 công cụ sắp xếp tiếng Việt hơn anh à!

Tất nhiên tôi cũng có

Mã:
Function Sort2DArray(ByVal Arr, ArrIndex, _
    col1 As Long, Optional ByVal sortAtoZ1 As Boolean = True, Optional ByVal convert1 As convert_col = cc_nothing, Optional ByVal phan_biet_hoa_thuong1 As Boolean = True, _
    Optional ByVal col2 As Long = -32768, Optional ByVal sortAtoZ2 As Boolean = True, Optional ByVal convert2 As convert_col = cc_nothing, Optional ByVal phan_biet_hoa_thuong2 As Boolean = True, _
    Optional ByVal col3 As Long = -32768, Optional ByVal sortAtoZ3 As Boolean = True, Optional ByVal convert3 As convert_col = cc_nothing, Optional ByVal phan_biet_hoa_thuong3 As Boolean = True)

Mà khi cần sort cột có tiếng Việt thì thiết lập thông số convert. Tất nhiên lúc đó code sẽ làm lâu hơn chút vì phải mã hóa các chuỗi.

QuickSort1DArray được sinh ra để sort mảng số hoặc chuỗi không chứa ký tự Việt. Vì có những tình huống như thế mà chả nhẽ giết gà lại dùng dao mổ trâu? Mà dùng dao mổ trâu dứt khoát chậm hơn. Cầm một con dao nhỏ thao tác vẫn tiện hơn cầm một con dao lớn.
QuickSort1DArray dứt khoát nhanh hơn vả lại nó chỉ dùng cho mảng 1 chiều.
--------------

Vì hàm của tôi viết cho dữ liệu đầu vào là mảng - tên là QuickSort1DArray mà - mà của các bạn là String nhưng tôi không muốn sửa vậy thì tôi chịu "thiệt thòi", tức tách từ chuỗi vào mảng sau đó khi có kết quả thì lại nối thành chuỗi.

Do máy tôi rất yếu nên tôi ngại test với dữ liệu lớn. Vậy các bạn test hộ code sau và thông báo nhé

À, giải thích thêm. Tại sao tôi không gọi
Mã:
QuickSort1DArray tmp, LBound(tmp), UBound(tmp), True

mà lại tạo Arr từ tmp rồi gọi

Mã:
QuickSort1DArray Arr, LBound(Arr), UBound(Arr), True

???

Bởi hàm của tôi sort cả mảng số lẫn mảng chuỗi. Mà vd.
3, 4, 5, 7, 8, 9 < 20
nhưng
"20" < "3", "4", "5", "7", "8", "9"

Các bạn dùng chuỗi nhưng các bạn lại lại "bắt" 3 < 20, tức coi 3 và 20 như số nên bắt buộc tôi phải sort mảng số. Vì tmp là mảng chuỗi nên tôi phải tạo mảng số Arr

Module1
Mã:
Private Declare Function GetTickCount Lib "kernel32.dll" () As Long

Function SortArrayScript(SerStr As String) As String
    SerStr = "('" & SerStr & "').split(',').sort(function(a,b){return (a-b)}).join(',')" ' & ".reverse()" 'if DESC
    With CreateObject("MSScriptControl.ScriptControl")
        .Language = "JavaScript"
        SortArrayScript = .Eval(SerStr)
    End With
End Function

Function SortArraySystem(SerStr As String) As String
    Dim str As String, Arr, i As Long
    Arr = Split(SerStr, ",")
    With CreateObject("System.Collections.ArrayList")
        For i = 0 To UBound(Arr)
            .Add CLng(Arr(i))
        Next
        .Sort: Arr = .ToArray
    End With
    SortArraySystem = Join(Arr, "-")
End Function

Public Sub QuickSort1DArray(Arr, iLo As Long, iHi As Long, ByVal sortAtoZ As Boolean)
'    Arr laĚ maŇng câĚn săěp xęěp
'    sortAtoZ xaěc điňnh caěch săěp xęěp tăng hay giaŇm
'    iLo laĚ câňn dýőěi cuŇa maŇng Arr, iHi laĚ câňn tręn cuŇa maŇng Arr
Dim Lo As Long, Hi As Long, iMid, DoChange As Boolean, s

    Do
        Lo = iLo
        Hi = iHi
        
        iMid = Arr((Lo + Hi) \ 2)
        Do
            If sortAtoZ Then
                Do While Arr(Lo) < iMid
                    Lo = Lo + 1
                Loop
                Do While Arr(Hi) > iMid
                    Hi = Hi - 1
                Loop
            Else
                Do While Arr(Lo) > iMid
                    Lo = Lo + 1
                Loop
                Do While Arr(Hi) < iMid
                    Hi = Hi - 1
                Loop
            End If
            
            If Lo <= Hi Then
                If sortAtoZ Then
                    DoChange = (Arr(Lo) > Arr(Hi))
                Else
                    DoChange = (Arr(Lo) < Arr(Hi))
                End If
                If DoChange Then
                    s = Arr(Lo)
                    Arr(Lo) = Arr(Hi)
                    Arr(Hi) = s
                End If
                
                Lo = Lo + 1
                Hi = Hi - 1
            End If
        Loop Until Lo > Hi
        If Hi > iLo Then QuickSort1DArray Arr, iLo, Hi, sortAtoZ
        iLo = Lo
    Loop Until Lo >= iHi
End Sub

Sub test()
Dim s As String, a As String, t As Double, k As Long, Arr() As Long, tmp, n As Long
    
    t = GetTickCount
    For k = 1 To 60000
        a = SortArrayScript("1,9,1,2,1,8,16,4,3,11,11,9,5,11,12,13,16,15,20,15,14,10,7,20")
    Next k
    Debug.Print "SortArrayScript:: " & (GetTickCount - t) / 1000 & " " & a

    t = GetTickCount
    For k = 1 To 60000
        a = SortArraySystem("1,9,1,2,1,8,16,4,3,11,11,9,5,11,12,13,16,15,20,15,14,10,7,20")
    Next k
    Debug.Print "SortArraySystem: " & (GetTickCount - t) / 1000 & " " & a
    
    t = GetTickCount
    For k = 1 To 60000
        a = ""
        tmp = Split("1,9,1,2,1,8,16,4,3,11,11,9,5,11,12,13,16,15,20,15,14,10,7,20", ",")
        ReDim Arr(LBound(tmp) To UBound(tmp))
        For n = LBound(tmp) To UBound(tmp)
            Arr(n) = tmp(n)
        Next n
        
        QuickSort1DArray Arr, LBound(Arr), UBound(Arr), True
        
        For n = LBound(tmp) To UBound(tmp)
            a = a & Arr(n) & "-"
        Next n
        a = Left(a, Len(a) - 1)
    Next k
    Debug.Print "QuickSort1DArray: " & (GetTickCount - t) / 1000 & " " & a
End Sub
 
Lần chỉnh sửa cuối:
Upvote 0
Hãy suy nghĩ đơn giản chút đi bạn, theo tui nghĩ thì việc viết không dùng vòng lặp có nghĩa là mình không được viết vòng lặp, nhưng không cấm việc sử dụng những công cụ mà bên trong nó có vòng lặp hay là không. Ta chả cần quan tâm bên trong nó có gì cho mệt xác, chỉ cần biết cái nào hợp, hay cảm thấy hay là dùng.

Chẳng qua là giải thích cho rõ vòng lặp nó nằm ở đâu thôi. Nhưng ...

Nếu nói về bài cụ thể này thì tôi có cảm giác là những chỗ đỏ đỏ gợi ý là: "Tôi cấm không được dùng vòng lặp "trực tiếp" (tức "gián tiếp thì được) vậy khi tôi không dùng vòng lặp "trực tiếp" thì tôi khoe"

Nếu tôi cảm giác đúng thì bạn hãy chỉ ra cho tôi trong tất cả các bài viết của Nghĩa chỗ nào nói cấm dùng vòng lặp?
 
Upvote 0
Hãy suy nghĩ đơn giản chút đi bạn, theo tui nghĩ thì việc viết không dùng vòng lặp có nghĩa là mình không được viết vòng lặp, nhưng không cấm việc sử dụng những công cụ mà bên trong nó có vòng lặp hay là không. Ta chả cần quan tâm bên trong nó có gì cho mệt xác, chỉ cần biết cái nào hợp, hay cảm thấy hay là dùng.

Chính suy nghĩ đơn giản đó mới dẫn đến hiểu nhầm, đến sai lầm bạn ah,

Phải chuẩn về mặt ngữ nghĩa khi nói về chuyên môn - nhất là lại đem đố (thành đề cho người khác) - tôi không nói bài riêng HTN đang đố mà nói chung về khái niệm sai lầm đó -- dẫn đến sự lầm tưởng cho người đọc, người áp dụng

Bạn hay ai thích đơn giản thì giữ riêng cho mình đừng viết ra khuyên người khác hay thách đố điều (đơn giản) sai đó được (dĩ nhiên có thể nói/ viết ra thì phải mở ngoặc giải thích rõ ràng - mà đã thế thì thà theo chuẩn chung phải hay hơn không). Vì đây là diễn đàn công khai cho cộng đồng, thì chúng ta phải chọn cái chuẩn chung cái đúng chung. Có như vậy mới giúp mọi người cùng tiến chứ không phải cứ hiểu ngầm định hay giả định quan niệm riêng của tôi.
 
Upvote 0
Module1
Mã:
Private Declare Function GetTickCount Lib "kernel32.dll" () As Long

vì chẳng bao giờ có chuỗi nào tách mà dài lê văn thê đến hàng 10 000 thành phần tức là vài chục ngàn ký tự cả (nếu thế thì gọi là phi thực tế)

Đọc bài #832 của bạn vodoi2x tôi giật mình: có thể Nghĩa đã nhập một chuỗi dài lê thê và sắp xếp 1 lần chứ không phải test 60000 lần một chuỗi ngắn.

Vậy thì ta test 1 lần với dữ liệu lớn. Nhưng như thế thì tôi thiệt thòi quá. Việc tách chuỗi vào mảng rồi từ kết quả nối chuỗi có "-" mất nhiều thời gian quá.

Ta thử cho đk là như nhau và xét thử với đk:

1. Chỉ đo thời gian cho việc sắp xếp thôi. Tức đo thời gian mà QuickSort1DArray và "System.Collections.ArrayList" đã dùng. Vì chuyện nối chuỗi bằng "-" hay "," là nhu cầu của bài toán cụ thể chứ không phải là chuyện của hàm Sort là hàm universal chỉ làm việc sort thôi.

2. Dữ liệu vào là mảng Long. Đã thử tốc độ thì làm luôn với mảng có nửa triệu số

3. Dữ liệu ra là mảng đã sắp xếp

4. Tôi sửa SortArraySystem để dữ liệu vào là mảng

Mã:
Private Declare Function GetTickCount Lib "kernel32.dll" () As Long

Function SortArraySystem(Arr)
    Dim i As Long
    With CreateObject("System.Collections.ArrayList")
        For i = LBound(Arr) To UBound(Arr)
            .Add Arr(i)
        Next
        .Sort
        SortArraySystem = .ToArray
    End With
End Function

Public Sub QuickSort1DArray(Arr, iLo As Long, iHi As Long, ByVal sortAtoZ As Boolean)
Dim Lo As Long, Hi As Long, iMid, DoChange As Boolean, s

    Do
        Lo = iLo
        Hi = iHi
        
        iMid = Arr((Lo + Hi) \ 2)
        Do
            If sortAtoZ Then
                Do While Arr(Lo) < iMid
                    Lo = Lo + 1
                Loop
                Do While Arr(Hi) > iMid
                    Hi = Hi - 1
                Loop
            Else
                Do While Arr(Lo) > iMid
                    Lo = Lo + 1
                Loop
                Do While Arr(Hi) < iMid
                    Hi = Hi - 1
                Loop
            End If
            
            If Lo <= Hi Then
                If sortAtoZ Then
                    DoChange = (Arr(Lo) > Arr(Hi))
                Else
                    DoChange = (Arr(Lo) < Arr(Hi))
                End If
                If DoChange Then
                    s = Arr(Lo)
                    Arr(Lo) = Arr(Hi)
                    Arr(Hi) = s
                End If
                
                Lo = Lo + 1
                Hi = Hi - 1
            End If
        Loop Until Lo > Hi
        If Hi > iLo Then QuickSort1DArray Arr, iLo, Hi, sortAtoZ
        iLo = Lo
    Loop Until Lo >= iHi
End Sub

Function Draw(Arr, Amount As Long)
Dim index As Long, k As Long, d As Long, c As Long, tmpArr, original

    If Amount > UBound(Arr) - LBound(Arr) + 1 Then Exit Function
    original = Arr
    
    ReDim tmpArr(1 To Amount)
    d = LBound(original)
    c = UBound(original)
    Randomize
    For k = 1 To Amount
        index = Int(Rnd() * (c - d + 1)) + d
        tmpArr(k) = original(index)
        original(index) = original(k + LBound(original) - 1)
        d = d + 1
    Next k
    Draw = tmpArr
End Function

Sub test()
Dim t As Double, k As Long, Arr, tmp, identic As Boolean, he
    ReDim Arr(1 To 500000)
    ReDim he(1 To 500000)
    
    For k = 1 To 500000
        Arr(k) = k
    Next k
    Arr = Draw(Arr, 500000)
    
    For k = 1 To 500000
        he(k) = Arr(k)
    Next k
'    ------------------------------------------------------------------    
    t = GetTickCount
    tmp = SortArraySystem(Arr)
    Debug.Print "SortArraySystem: " & (GetTickCount - t) / 1000
    
    t = GetTickCount
    QuickSort1DArray he, LBound(he), UBound(he), True
    Debug.Print "QuickSort1DArray: " & (GetTickCount - t) / 1000
    
'    kiem tra xem 2 mang tmp va Arr co nhu nhau khong
    identic = True
    For k = 1 To 500000
        If tmp(k - 1) <> he(k) Then
            identic = False
            Exit For
        End If
    Next k
    
    Debug.Print "identic = " & identic
End Sub
 
Lần chỉnh sửa cuối:
Upvote 0
2. Dữ liệu vào là mảng Long. Đã thử tốc độ thì làm luôn với mảng có nửa triệu số

3. Dữ liệu ra là mảng đã sắp xếp

4. Tôi sửa SortArraySystem để dữ liệu vào là mảng
kết quả em test là như sau :
[INFO1]
SortArraySystem: 16.469 (1)
QuickSort1DArray: 7.453
identic = True[/INFO1]
Em nghĩ SortArraySystem sở dĩ lâu hơn vì nó phải mất công thực hiện 2 công việc :
*1 khai báo trễ createObject
* Tốn 1 vòng for để lại đưa từng phần tử mảng vào ArrayList ( .Add Arr(i))
--> em test lại như sau :
[GPECODE=vb]
Function SortArraySystem(Arr)
Dim i As Long
With CreateObject("System.Collections.ArrayList")
For i = LBound(Arr) To UBound(Arr)
.Add Arr(i)
Next
'.Sort
SortArraySystem = .ToArray
End With
End Function
[/GPECODE]
[GPECODE=vb]
Sub ktr()
Dim t As Double, k As Long, Arr
ReDim Arr(1 To 500000)
For k = 1 To 500000
Arr(k) = k
Next k
Arr = Draw(Arr, 500000)
t = GetTickCount
tmp = SortArraySystem(Arr)
Debug.Print "SortArraySystem: " & (GetTickCount - t) / 1000
End Sub
[/GPECODE]
kết quả là :SortArraySystem: 13.781 (2)

Tức là theo em phương thức .sort của System.Collections.ArrayList sẽ là =(1)-(2) =16.469-13.781=2.6(s) --> tốc độ quá khủng chăng !$@!!

theo em hiểu thì cách sắp xếp của anh là phân đoạn
Mã:
iMid = Arr((Lo + Hi) \ 2)

-trường hợp xấu nhất là :iMid sẽ là giá trị lớn nhất , hoặc nhỏ nhất trong đoạn chia từ Lo đến Hi
- Nếu đẹp nhất là IMid bằng giá trị trung bình trong đoạn Lo Hi

---> em thử sửa iMid = INT[Rnd(Hi-Li+1)+Li] thì có lúc nhanh lúc chậm ,(có lúc chỉ còn 6,2 s < QuickSort1DArray =7.453)--> có cách nào chọn Imid tối ưu nhất không anh !

Thanks anh !
 
Lần chỉnh sửa cuối:
Upvote 0
kết quả em test là như sau :
[INFO1]
SortArraySystem: 16.469 (1)
QuickSort1DArray: 7.453
identic = True[/INFO1]

Trên máy cũ kỹ của tôi kết quả hơi khác, thường là SortArraySystem/QuickSort1DArray ≈ 7

theo em hiểu thì cách sắp xếp của anh là phân đoạn
Mã:
iMid = Arr((Lo + Hi) \ 2)

-trường hợp xấu nhất là :iMid sẽ là giá trị lớn nhất , hoặc nhỏ nhất trong đoạn chia từ Lo đến Hi
- Nếu đẹp nhất là IMid bằng giá trị trung bình trong đoạn Lo Hi

---> em thử sửa iMid = INT[Rnd(Hi-Li+1)+Li] thì có lúc nhanh lúc chậm ,(có lúc chỉ còn 6,2 s < QuickSort1DArray =7.453)--> có cách nào chọn Imid tối ưu nhất không anh !

Quick sort nó là thế.
Nếu bạn định xét iMid với kiểu chọn tối ưu thì xin mời tự nghĩ ra thuật toán mới nhé.
Không có kiểu sort nào hoàn hảo 100%, trong mọi trường hợp cả. Bởi nếu thế thì có "ngần ấy" cách sort để mà làm gì? Chỉ có kiểu A tới ưu hơn B mà thôi.

À, bạn hãy chạy thử thêm code sau - sort tăng dần một mảng đã được sắp xếp giảm dần

Kết quả của tôi là

Mã:
SortArraySystem: 60,417
QuickSort1DArray: 4,947
identic = True

SortArraySystem: 62,48
QuickSort1DArray: 5,719
identic = True

SortArraySystem: 63,681
QuickSort1DArray: 4,547
identic = True

Code dùng cho test

Mã:
Sub test()
Dim t As Double, k As Long, Arr, tmp, identic As Boolean, he
    ReDim Arr(1 To 500000)
    ReDim he(1 To 500000)
    
    For k = 1 To 500000
        Arr(k) = 500001 - k
    Next k
    
    For k = 1 To 500000
        he(k) = Arr(k)
    Next k
'    ------------------------------------------------------------------
    t = GetTickCount
    tmp = SortArraySystem(Arr)
    Debug.Print "SortArraySystem: " & (GetTickCount - t) / 1000
    
    t = GetTickCount
    QuickSort1DArray he, LBound(he), UBound(he), True
    Debug.Print "QuickSort1DArray: " & (GetTickCount - t) / 1000
    
'    kiem tra xem 2 mang tmp va Arr co nhu nhau khong
    identic = True
    For k = 1 To 500000
        If tmp(k - 1) <> he(k) Then
            identic = False
            Exit For
        End If
    Next k
    
    Debug.Print "identic = " & identic
End Sub

Và nếu muốn thì cả sort tăng dần một mảng đã được sắp xếp tăng dần

Tức thay

Mã:
For k = 1 To 500000
        Arr(k) = [B][COLOR=#ff0000]500001 -[/COLOR][/B] k
    Next k
    
    For k = 1 To 500000
        he(k) = Arr(k)
    Next k

bằng

Mã:
For k = 1 To 500000
        Arr(k) = k
    Next k
    
    For k = 1 To 500000
        he(k) = Arr(k)
    Next k
 
Lần chỉnh sửa cuối:
Upvote 0
Trên máy cũ kỹ của tôi kết quả hơi khác, thường là SortArraySystem/QuickSort1DArray ≈ 7



Quick sort nó là thế.
Nếu bạn định xét iMid với kiểu chọn tối ưu thì xin mời tự nghĩ ra thuật toán mới nhé.
Không có kiểu sort nào hoàn hảo 100%, trong mọi trường hợp cả. Bởi nếu thế thì có "ngần ấy" cách sort để mà làm gì? Chỉ có kiểu A tới ưu hơn B mà thôi.

À, bạn hãy chạy thử thêm code sau - sort tăng dần một mảng đã được sắp xếp giảm dần

Kết quả của tôi là

Mã:
SortArraySystem: 60,417
QuickSort1DArray: 4,947
identic = True

SortArraySystem: 62,48
QuickSort1DArray: 5,719
identic = True

SortArraySystem: 63,681
QuickSort1DArray: 4,547
identic = True

Code dùng cho test

Mã:
Sub test()
Dim t As Double, k As Long, Arr, tmp, identic As Boolean, he
    ReDim Arr(1 To 500000)
    ReDim he(1 To 500000)
    
    For k = 1 To 500000
        Arr(k) = 500001 - k
    Next k
    
    For k = 1 To 500000
        he(k) = Arr(k)
    Next k
'    ------------------------------------------------------------------
    t = GetTickCount
    tmp = SortArraySystem(Arr)
    Debug.Print "SortArraySystem: " & (GetTickCount - t) / 1000
    
    t = GetTickCount
    QuickSort1DArray he, LBound(he), UBound(he), True
    Debug.Print "QuickSort1DArray: " & (GetTickCount - t) / 1000
    
'    kiem tra xem 2 mang tmp va Arr co nhu nhau khong
    identic = True
    For k = 1 To 500000
        If tmp(k - 1) <> he(k) Then
            identic = False
            Exit For
        End If
    Next k
    
    Debug.Print "identic = " & identic
End Sub

Và nếu muốn thì cả sort tăng dần một mảng đã được sắp xếp tăng dần

Tức thay

Mã:
For k = 1 To 500000
        Arr(k) = [B][COLOR=#ff0000]500001 -[/COLOR][/B] k
    Next k
    
    For k = 1 To 500000
        he(k) = Arr(k)
    Next k

bằng

Mã:
For k = 1 To 500000
        Arr(k) = k
    Next k
    
    For k = 1 To 500000
        he(k) = Arr(k)
    Next k

Sau khi test xong, tốc độ thật đáng nể!

Mục đích của em là SORT, tìm những thủ tục, những hàm tối ưu, dự kiến sau này sẽ phát triển sắp xếp dữ liệu trên Excel 2007 trở về sau (hơn 1 triệu hàng), vì thế mới tung chiêu đố.

Tuy nhiên, em vẫn đánh giá cao SortArraySystem vì nó Sort vừa chuỗi vừa số được, chẳng hạn sắp xếp từ chuỗi này:

str = "a,9,8,c,6,7,b,3,a,f,4,6,7,e,10,1,11,33"

Kết quả = 1,10,11,3,33,4,6,6,7,7,8,9,a,a,b,c,e,f

Không biết hàm mình viết có được vậy không!? Hay có thể là hay hơn (sắp xếp chỗ tô đậm cho đúng vị trí)?

'---------------------------------------------------------

Hỏi xong mới được đọc bài này:

Hàm dùng cho sắp xếp mảng - nhờ test hộ và góp ý


Giờ mới thấy bài đó, lẽ ra phải thấy sớm hơn thì khỏi phải đố điếc chi cho mệt nhỉ? Cám ơn Thầy siwtom nhé!
 
Lần chỉnh sửa cuối:
Upvote 0
Tuy nhiên, em vẫn đánh giá cao SortArraySystem vì nó Sort vừa chuỗi vừa số được, chẳng hạn sắp xếp từ chuỗi này:

str = "a,9,8,c,6,7,b,3,a,f,4,6,7,e,10,1,11,33"

Không phải là "Sort vừa chuỗi vừa số được"

Hàm
Mã:
Function SortArraySystem(SerStr As String) As String
    Dim str As String, Arr, i As Long
    Arr = Split(SerStr, ",")
    With CreateObject("System.Collections.ArrayList")
        For i = 0 To UBound(Arr)
            .Add CLng(Arr(i))
        Next
        .Sort: Arr = .ToArray
    End With
    SortArraySystem = Join(Arr, "-")
End Function

là sắp xếp mảng CHUỖI.
Do Arr được cắt từ chuỗi - Arr = Split(SerStr, ",") - nên nó là mảng chuỗi - mỗi phần tử là chuỗi. Tức vd. ta có "11" chứ không phải 11.

Nhưng trong code
Mã:
Function SortArraySystem(Arr)
    Dim i As Long
    With CreateObject("System.Collections.ArrayList")
        For i = LBound(Arr) To UBound(Arr)
            .Add Arr(i)
        Next
        .Sort
        SortArraySystem = .ToArray
    End With
End Function

Sub test()
Dim k As Long, Arr, tmp
    ReDim Arr(1 To 500000)
    
    For k = 1 To 500000
        Arr(k) = k
    Next k
    Arr = Draw(Arr, 500000)
    tmp = SortArraySystem(Arr)
End Sub

thì ta đã truyền vào hàm SortArraySystem một mảng số. Vì kiểu của mỗi phần tử là số (do Arr(k) = k mà k là Long)
Kết quả = 1,10,11,3,33,4,6,6,7,7,8,9,a,a,b,c,e,f

Không biết hàm mình viết có được vậy không!?
Thì hàm QuickSort1DArray chứ còn hàm nào? Chỉ có điều dữ liệu vào là mảng CHUỖI.

Nhìn thứ tự các số thì hiểu tại sao tôi lại viết trong bài #833
À, giải thích thêm. Tại sao tôi không gọi
Mã:
QuickSort1DArray tmp, LBound(tmp), UBound(tmp), True

mà lại tạo Arr từ tmp rồi gọi

Mã:
QuickSort1DArray Arr, LBound(Arr), UBound(Arr), True
???

Bởi hàm của tôi sort cả mảng số lẫn mảng chuỗi. Mà vd.
3, 4, 5, 7, 8, 9 < 20
nhưng
"20" < "3", "4", "5", "7", "8", "9"

Các bạn dùng chuỗi nhưng các bạn lại lại "bắt" 3 < 20, tức coi 3 và 20 như số nên bắt buộc tôi phải sort mảng số. Vì tmp là mảng chuỗi nên tôi phải tạo mảng số Arr

Bạn nào không hiểu tại sao lại có thứ tự 11, 3, 33, 4 thì tìm hiểu về cách so sánh chuỗi nhé.

Mã:
Sub test()
Dim Arr, s As String
    s = "a,9,8,c,6,7,b,3,a,f,4,6,7,e,10,1,11,33"
    Arr = Split(s, ",")
    
    QuickSort1DArray Arr, LBound(Arr), UBound(Arr), True
    s = Join(Arr, ",")
    Debug.Print s
End Sub

Giờ mới thấy bài đó

Thế là thấm thoát hơn 10 tháng đã trôi qua ...
 
Upvote 0
kết quả em test là như sau :
[INFO1]
SortArraySystem: 16.469 (1)
QuickSort1DArray: 7.453
identic = True[/INFO1]
Em nghĩ SortArraySystem sở dĩ lâu hơn vì nó phải mất công thực hiện 2 công việc :
*1 khai báo trễ createObject
* Tốn 1 vòng for để lại đưa từng phần tử mảng vào ArrayList ( .Add Arr(i))

Tôi không nghĩ vậy!
Cái mình viết chỉ CHUYÊN DÙNG ĐỂ SORT (đâu có làm gì khác hơn)
Thằng System.Collections.ArrayList là 1 công cụ đồ sộ, nó đâu chỉ có SORT
Vậy nên nếu phần sort cho tốc độ thấp hơn cũng là chuyện quá bình thường
Từ xưa tôi vẫn biết cái QuickSort của anh siwtom nó nhanh nhưng phải nói thằng thế này:
- Rất nhiều người lúng túng khi dùng QụickSort của anh siwtom (em nói thật, mong anh đừng buồn). Có thể vì cách viết của anh chưa "gần gũi" chẳng?
- Với System.Collections.ArrayList: Đơn giản + tốc độ chấp nhận được + Nhìn là hiểu + áp dụng được ngay
 
Upvote 0
Tôi không nghĩ vậy!
Cái mình viết chỉ CHUYÊN DÙNG ĐỂ SORT (đâu có làm gì khác hơn)
Thằng System.Collections.ArrayList là 1 công cụ đồ sộ, nó đâu chỉ có SORT
Vậy nên nếu phần sort cho tốc độ thấp hơn cũng là chuyện quá bình thường
Từ xưa tôi vẫn biết cái QuickSort của anh siwtom nó nhanh nhưng phải nói thằng thế này:
- Rất nhiều người lúng túng khi dùng QụickSort của anh siwtom (em nói thật, mong anh đừng buồn). Có thể vì cách viết của anh chưa "gần gũi" chẳng?
- Với System.Collections.ArrayList: Đơn giản + tốc độ chấp nhận được + Nhìn là hiểu + áp dụng được ngay

Nếu chỉ nói về dùng thì hàm chỉ vỏn vện có 4 đối số. Ý nghĩa của chúng thế nào thì tôi có giải thích. Vậy lúng túng khi dùng là thế nào?

Bạn có thể nói rõ hơn cái "gần gũi" không? Nếu nói code sai, code viết chưa tối ưu ... thì đều có thể tranh luận được. Vì code thế nào thì ai cũng nhìn thấy. Còn nói chưa "gần gũi" thì chịu rồi. Chưa gần gũi là cảm nhân chủ quan, là cái có muôn vàn bộ mặt. Cái với anh A là gần gũi có thể với anh B là không gần gũi.
Với cái chưa "gần gũi" đó thì bó tay rồi.
 
Upvote 0
Nếu chỉ nói về dùng thì hàm chỉ vỏn vện có 4 đối số. Ý nghĩa của chúng thế nào thì tôi có giải thích. Vậy lúng túng khi dùng là thế nào?

Bạn có thể nói rõ hơn cái "gần gũi" không? Nếu nói code sai, code viết chưa tối ưu ... thì đều có thể tranh luận được. Vì code thế nào thì ai cũng nhìn thấy. Còn nói chưa "gần gũi" thì chịu rồi. Chưa gần gũi là cảm nhân chủ quan, là cái có muôn vàn bộ mặt. Cái với anh A là gần gũi có thể với anh B là không gần gũi.
Với cái chưa "gần gũi" đó thì bó tay rồi.

Đây anh
Mã:
Public Sub QuickSort1DArray(Arr, iLo As Long, iHi As Long, ByVal sortAtoZ As Boolean)
Em vẫn chưa hiểu biến iLo, iHi để làm gì?
Đứng trên cương vị người dùng thì em chỉ cần thế này:
Mã:
Public Sub QuickSort1DArray(arr, ByVal sortAtoZ As Boolean)
Tức sort mảng nào, và sort tăng dần hay giảm dần
Vậy thôi em nghĩ là đủ rồi
(Hay không còn có ý gì khác về 2 biến trên)
 
Upvote 0
Đây anh
Mã:
Public Sub QuickSort1DArray(Arr, iLo As Long, iHi As Long, ByVal sortAtoZ As Boolean)
Em vẫn chưa hiểu biến iLo, iHi để làm gì?
Đứng trên cương vị người dùng thì em chỉ cần thế này:
Mã:
Public Sub QuickSort1DArray(arr, ByVal sortAtoZ As Boolean)
Tức sort mảng nào, và sort tăng dần hay giảm dần
Vậy thôi em nghĩ là đủ rồi
(Hay không còn có ý gì khác về 2 biến trên)

Em thì hiểu như vậy , không biết có đúng không :
iLo = integer Low, iHi = integer Hight --> tức là chỉ số trên và dưới của một đoạn phần tử trong mảng : Arr(iLo) : Arr(iHi)
* Vì đây là cách viết theo thuật toán đệ quy --> bắt buộc phải có 2 đối số iLo và iHi trong QuickSort
 
Upvote 0
Em thì hiểu như vậy , không biết có đúng không :
iLo = integer Low, iHi = integer Hight --> tức là chỉ số trên và dưới của một đoạn phần tử trong mảng : Arr(iLo) : Arr(iHi)
* Vì đây là cách viết theo thuật toán đệ quy --> bắt buộc phải có 2 đối số iLo và iHi trong QuickSort

Không biết bạn nói có đúng không... nhưng nếu đúng là vậy thì rõ ràng 2 biến này gây lúng túng cho người dùng ---> Rõ ràng tôi không cần nó để làm gì
 
Upvote 0
Em thì hiểu như vậy , không biết có đúng không :
iLo = integer Low, iHi = integer Hight --> tức là chỉ số trên và dưới của một đoạn phần tử trong mảng : Arr(iLo) : Arr(iHi)
* Vì đây là cách viết theo thuật toán đệ quy --> bắt buộc phải có 2 đối số iLo và iHi trong QuickSort

Hình như tôi cũng đã thấy 2 biến đó nó thừa thừa sao đó, nên đã viết ở bài này:

http://www.giaiphapexcel.com/forum/...nhờ-test-hộ-và-góp-ý&p=521349#post521349

Có thể về nguyên tắc của thuật toán nào đó thì phải bắt buộc như thế, nhưng với VBA của Excel thì không cần, vậy thì ta cứ giản thiểu biến một cách không cần thiết là được chứ nhỉ?
 
Upvote 0
Hình như tôi cũng đã thấy 2 biến đó nó thừa thừa sao đó, nên đã viết ở bài này:

http://www.giaiphapexcel.com/forum/...nhờ-test-hộ-và-góp-ý&p=521349#post521349

Có thể về nguyên tắc của thuật toán nào đó thì phải bắt buộc như thế, nhưng với VBA của Excel thì không cần, vậy thì ta cứ giản thiểu biến một cách không cần thiết là được chứ nhỉ?
Em không hiểu ý anh lắm, em lấy 1 ví dụ ngắn gọn thế này !
[GPECODE=vb]
Sub quicksort(subArr(), ByVal L As Long, ByVal U As Long)
Dim i As Long, j As Long
Dim pivot, TG
If L >= U Then Exit Sub <--- Đây là điểm neo, nếu không có điểm này sẽ là vòng lặp vô tận
i = L: j = U
pivot = Int(Rnd() * (j - i + 1) + i)
Do
While subArr(i) < subArr(pivot)
i = i + 1
Wend
While subArr(j) > subArr(pivot)
j = j - 1
Wend
If i <= j Then
TG = subArr(i): subArr(i) = subArr(j): subArr(j) = TG
i = i + 1
j = j - 1
End If
Loop Until i > j
quicksort subArr, L, j <---------- Đệ quy với tham số L,J
quicksort subArr, i, U<---------- Đệ quy với tham số i,U
End Sub
Nếu mà không có 2 tham số L,U thì anh phải tạo thêm 1 điểm neo nữa
[/GPECODE]
Tài liệu tham khảo trên google :
1. Sơ lược
Giải thuật Quick Sort là một phương pháp sắp xếp theo kiểu phân đoạn được cải tiến từ thuật toán Selection Sort do C.A.R Hoare đưa ra. Cụ thể như sau:

Đầu tiên cần chọn một phần tử nào đó của dãy làm chốt hay phần tử quay (pivot).
Trong bước tiếp theo, các phần tử nhỏ hơn chốt phải được xếp vào phía trước chốt (đầu dãy) và các
phần tử lớn hơn được xếp vào phía sau chốt (cuối dãy). Để có được sự phân loại này, các phần tử sẽ được so
sánh với chốt và hoán đổi vị trí cho nhau hoặc cho chốt nếu nó lớn hơn chốt mà lại nằm trước
hoặc nhỏ hơn chốt mà lại nằm sau. Khi lượt hoán đổi đầu tiên thực hiện xong thì dãy được chia
thành 2 đoạn: 1 đoạn bao gồm các phần tử nhỏ hơn chốt, đoạn còn lại bao gồm các phần tử lớn
hơn chốt, còn chốt chính là phần tử đã được sắp xếp.
Áp dụng kĩ thuật trên cho từng phân đoạn cho tới khi mỗi đoạn chỉ còn lại 2 phần tử thì việc sắp xếp kết thúc.
2. Ví dụ
Sắp xếp dãy sau bằng giải thuật Quick Sort:
Chỉ số : 0 1 2 3 4 5 6 7 8 9
Mảng A : 37 11 25 44 55 28 98 84 54 73
Ta chọn chốt đầu tiên là 37. Để tìm 2 khóa cần đổi chỗ cho nhau, ta dùng 2 biến i và j với giá trị ban đầu: i=1, j=9.
Nếu Ai<37 thì tiếp tục tăng i lên 1 và lặp lại, cho tới khi gặp phần tử Ai>37.
Duyệt các phần tử Aj, nếu Aj>37 thì giảm j đi một, cho tới khi gặp phần tử Aj<37.
Nếu i còn nhỏ hơn j thì đổi chỗ Ai và Aj tìm được cho nhau.
Quá trình lặp lại với Ai và Aj cho đến khi i=j, chính là vị trí dành cho khóa 37.
Cuối cùng ta đổi chỗ 37 cho khóa Aj.
Lượt đầu tiên i dừng lại ở A3=44, j dừng lại ở A5=28.
37 11 25 44 55 28 98 84 54 73
Chốt -----I -----j

Đổi chỗ 44 và 28:
37 11 25 28 55 44 98 84 54 73
------------I ---j

Tiếp tục chạy Ai từ 28 và Aj từ 44, i dừng lại ở A4=55, j dừng lại ở A3=28:
37 11 25 28 55 44 98 84 54 73
----------J-- i
Lúc này i>j, ta đổi chỗ chốt 37 cho Aj=28 được:
28 11 25 37 55 44 98 84 54 73
Như vậy kết thúc lần thứ nhất ta được hai đoạn được phân biệt bởi khóa 37 như sau:
28 11 25 [37] 55 44 98 84 54 73

Quá trình được lặp lại tương tự cho từng phân đoạn trên cho tới khi dãy được sắp xếp hoàn toàn
 
Lần chỉnh sửa cuối:
Upvote 0
Em không hiểu ý anh lắm, em lấy 1 ví dụ ngắn gọn thế này !
[GPECODE=vb]
Sub quicksort(subArr(), ByVal L As Long, ByVal U As Long)
Dim i As Long, j As Long
Dim pivot, TG
If L >= U Then Exit Sub <--- Đây là điểm neo, nếu không có điểm này sẽ là vòng lặp vô tận
i = L: j = U
pivot = Int(Rnd() * (j - i + 1) + i)
Do
While subArr(i) < subArr(pivot)
i = i + 1
Wend
While subArr(j) > subArr(pivot)
j = j - 1
Wend
If i <= j Then
TG = subArr(i): subArr(i) = subArr(j): subArr(j) = TG
i = i + 1
j = j - 1
End If
Loop Until i > j
quicksort subArr, L, j <---------- Đệ quy với tham số L,J
quicksort subArr, i, U<---------- Đệ quy với tham số L,J
End Sub
Nếu mà không có 2 tham số L,U thì anh phải tạo thêm 1 điểm neo nữa
[/GPECODE]
Vậy thôi ta tìm cách "quăng" biến ấy ra ngoài ở dạng Public hay gì đó đi
Nói chung là tôi không có khả năng sửa code, chỉ đứng trên QUAN ĐIỂM NGƯỜI DÙNG mà ĐÒI HỎI thôi
Ẹc... Ẹc...
 
Upvote 0
Web KT
Back
Top Bottom