Partager via


Visual Sorting Algorithm comparison

When I bought my first IBM PC around 1981, I wrote a program to demonstrate the speed of various sorting algorithms. It filled the screen with random characters, then the user could choose amongst a few sorting techniques and watch the sort algorithm in action as the data moved around on the screen. Watching the data move helps to see how the algorithm works.

The program could access the graphics card directly, writing characters directly into the video memory. For Foxpro, I used the @..SAY command to put a string at a particular row and column. For VB I used the Graphics.DrawString method.

Below are a Fox and VB reimplementation of this demo.

Run the code and type “B” for Bubble Sort, i=Insertion Sort, s=Shell Sort, q=Quick Sort. R will reset the data to be random.

Which one is fastest for you?

Some of the sorts take a *long* time so just the first few elements are sorted.

I’ve removed the Supersort code as an exercise for the reader: what 14 lines of code will achieve the result *much* faster than any other sorting algorithm?

Hint: none of the other algorithms require much additional storage other than a few local variables. The SuperSort uses an array of size 26.

CLEAR

PUBLIC ox

ox= NEWOBJECT("SortForm")

DEFINE CLASS SortForm AS Form

          left=200

          width=800

          height=600

          nRows=0

          nCols=0

          FontName="Courier New"

          FontSize=10

          BackColor=0xffffff

          DIMENSION arData[1]

          nTotal=0

          PROCEDURE init

* RAND(SECONDS()) && randomize generator

                   this.show

                   this.Setup

          PROCEDURE Setup

                   this.nRows=INT(this.height/FONTMETRIC(1)-1)

                   this.nCols=INT(this.width/FONTMETRIC(6))

                   this.nTotal= this.nRows*this.nCols

                   DIMENSION this.arData[this.nTotal]

                   this.Shuffle

          PROCEDURE Shuffle

                   FOR i = 1 TO this.nTotal

                             this.arData[i]=CHR(RAND()*26+ASC('A'))

                             @ INT((i-1)/this.nCols), INT((i-1)%this.nCols) say this.arData[i]

                   ENDFOR

                   this.Caption="# elements = "+TRANSFORM(thisform.nTotal)+" Try Bubble, Insertion, Shell, Quick sorts"

          PROCEDURE Resize

                   this.Cls

                   this.Setup

          PROCEDURE KeyPress(nKeyCode, nShiftAltCtrl)

                   IF nKeyCode=27 && <escape> Exit program

                             thisform.Release

                             RETURN

                   ENDIF

                   IF nKeyCode=ASC("r") && reset random data

                             thisform.Setup

                             RETURN

                   ENDIF

                   cSort=""

                   nMax=thisform.nTotal

                   DO CASE

                   CASE nKeyCode=ASC("b") && Bubble

                             cSort="Bubble"

                             nMax=MIN(thisform.nTotal,1000) && slow sort: limit # of items

                   CASE nKeyCode=ASC("i") && Insertion

                             cSort="Insertion"

                             nMax=MIN(thisform.nTotal,1000) && slow sort: limit # of items

                   CASE nKeyCode=ASC("s") && Shell Sort

                             cSort="Shell"

                   CASE nKeyCode=ASC("q") && Quick Sort

                             cSort="Quick"

                   CASE nKeyCode=ASC("x") &&Super Sort

                             cSort="Super"

                   OTHERWISE

                             MESSAGEBOX("Unknown command")

                             RETURN

                   ENDCASE

                  

                   this.Caption="# elements = "+TRANSFORM(nMax)+" Starting "+cSort+" Sort"

                   nStart = SECONDS()

                   this.&cSort.Sort(1,nMax) && Call the sort routine

                   this.Caption="# elements = "+TRANSFORM(thisform.nTotal)+" "+cSort+ " "+TRANSFORM(SECONDS()-nStart,"999.999")

          PROCEDURE Swap(nPos1,nPos2) && Exchange the positions of 2 elements

                   LOCAL cTemp

                   cTemp=this.arData[nPos1]

                   this.arData[nPos1]=this.arData[nPos2]

                   this.arData[nPos2]= cTemp

                   @ INT((nPos1-1)/this.nCols), INT((nPos1-1)%this.nCols) say this.arData[nPos1]

                   @ INT((nPos2-1)/this.nCols), INT((nPos2-1)%this.nCols) say this.arData[nPos2]

          PROCEDURE BubbleSort(nStart,nMax)

                   LOCAL i,j

                   FOR i = 1 TO nMax && loop through all elements

                             FOR j= 1 TO i && loop through current pos

                                      IF this.arData[i] < this.arData[j]

                                                this.Swap(i,j)

                                      ENDIF

                             ENDFOR

                   ENDFOR

          PROCEDURE InsertionSort(nStart,nMax)

                   LOCAL i, j,t

                   FOR j = 2 TO nMax

                             IF this.arData[j-1] > this.arData[j] && compare adjacent elements

                                      t = this.arData[j]

                                      FOR i = j TO 2 STEP -1 && make room by moving the rest down

                                                this.arData[i]=this.arData[i-1]

                                                @ INT((i-1)/this.nCols), INT((i-1)%this.nCols) say this.arData[i-1]

                                                IF this.arData[i-1] <= t

                                                          EXIT

                                                ENDIF

                                      ENDFOR

                                      this.arData[i]=t

                                      @ INT((i-1)/this.nCols), INT((i-1)%this.nCols) say t

                             ENDIF

                   ENDFOR

          PROCEDURE ShellSort(nStart,nMax)

                   LOCAL g,i,j

                   g = INT(nMax/2)

                   DO WHILE g > 0 && g is successively half of nMax

                             FOR i = g+1 TO nMax

                                      j = i - g

                                      DO WHILE j>0 AND this.arData[j] > this.arData[j+g] && do we swap?

                                                this.Swap(j,j+g)

                                                j=j-g && next group

                                      ENDDO

                             ENDFOR

                             g=INT(g/2)

                   ENDDO

          PROCEDURE QuickSort(nLeft,nRight) && left and right pointers into data. Divide and conquer

                   LOCAL cKey,i,j

                   IF nLeft >= nRight && if the pointers cross, then we're done

                             RETURN

                   ENDIF

  cKey = this.arData[nLeft] && the key is the first element

                   i=nLeft && start the left and right pointers

                   j = nRight +1

                   DO WHILE j > i && as the pointers move toward each other without crossing

                             i=i+1

                             DO WHILE this.arData[i] < cKey && move the left pointer til we find one out of pos

                                      i=i+1

                             ENDDO

                             j=j-1

                             DO WHILE this.arData[j] > cKey && move the right pointer til we find one out of pos

                                      j=j-1

                             ENDDO

                             IF j > i

                                      this.Swap(j,i) && swap them

                             ENDIF

                   ENDDO

                   this.Swap(j,nLeft) && now we know the key goes into position nleft

                   this.QuickSort(nLeft, j-1) && sort left & right sides.

                   this.QuickSort(j+1, nRight)

          PROCEDURE SuperSort(nStart,nMax)

                   *What 14 lines of superfast code should go here to accomplish the task of sorting all the data?

ENDDEFINE

This is the VB version:

Public Class Form1

    Public arData(1) ' VB arrays are 0 to n-1

    Dim rand As Random = New Random()

    Dim nRows As Integer

    Dim nCols As Integer

    Dim nTotal As Integer

    Delegate Sub dlgSortFunc(ByVal nStart As Integer, ByVal nMax As Integer)

    Private Sub Form1_Load(ByVal sender As Object, ByVal e As System.EventArgs) Handles Me.Load

        Me.Font = New Font("Courier new", 10)

        Me.BackColor = Color.White

        Me.Width = 800

        Me.Height = 600

        Me.Text = "Sort demo b=Bubble, i=Insertion, s=Shell, q=Quick sorts r=reset"

    End Sub

    Sub SetUp()

        nRows = Me.Height / Font.Height - 3

        nCols = Me.Width / Font.Size - 2

        nTotal = nRows * nCols

        ReDim arData(nTotal - 1)

        Dim g As Graphics = Graphics.FromHwnd(Me.Handle)

        g.FillRectangle(Brushes.White, 0, 0, Me.Width, Me.Height)

        g.Dispose()

        Shuffle()

    End Sub

    Sub Shuffle()

        Dim i As Integer

        For i = 0 To nTotal - 1

            arData(i) = Chr(rand.Next(0, 26) + Asc("A"))

            ShowChar(arData(i), i)

        Next

    End Sub

    Sub ShowChar(ByVal s As String, ByVal nPos As Integer)

        Dim x, y As Integer

        Dim g As Graphics

        g = Graphics.FromHwnd(Me.Handle)

        x = Int((nPos Mod Me.nCols)) * Me.Font.Size

        y = Int((nPos / Me.nCols)) * Me.Font.Height

        g.FillRectangle(Brushes.White, x, y, Me.Font.Size, Me.Font.Height)

        g.DrawString(s, Me.Font, Brushes.Black, x, y)

        g.Dispose()

    End Sub

    Private Sub Form1_Paint(ByVal sender As Object, ByVal e As System.Windows.Forms.PaintEventArgs) Handles Me.Paint

        Dim g As Graphics = Graphics.FromHwnd(Me.Handle)

        g.FillRectangle(Brushes.White, 0, 0, Me.Width, Me.Height)

        g.Dispose()

        Me.SetUp()

    End Sub

    Private Sub Form1_KeyPress(ByVal sender As Object, ByVal e As System.Windows.Forms.KeyPressEventArgs) Handles Me.KeyPress

        If Asc(e.KeyChar) = 27 Then ' esc: end program

            End

        End If

        If e.KeyChar = "r" Then

            SetUp()

            Return

        End If

        Dim cSort As String = ""

        Dim nMax As Integer = Me.nTotal - 1

        Dim MyDlg As dlgSortFunc

        Select Case e.KeyChar

            Case "b" ' bubble sort

                cSort = "Bubble"

                nMax = Math.Min(nMax, 800)

                MyDlg = AddressOf Me.BubbleSort

            Case "i" ' insertion

                cSort = "Insertion"

                nMax = Math.Min(nMax, 800)

                MyDlg = AddressOf Me.InsertionSort

            Case "s"

                cSort = "Shell"

                MyDlg = AddressOf Me.ShellSort

            Case "q"

                cSort = "Quick"

                MyDlg = AddressOf Me.QuickSort

            Case "x"

                cSort = "Super"

                MyDlg = AddressOf Me.SuperSort

            Case Else

                MsgBox("Unknown command")

                Return

        End Select

        Me.Text = String.Format("{0} Sort # elements = {1}", cSort, nMax + 1)

        Dim nStart As Integer = My.Computer.Clock.TickCount

        MyDlg(0, nMax)

        Dim nEnd As Integer = My.Computer.Clock.TickCount - nStart

        Me.Text = String.Format("{0} Sort # elements = {1} Time={2:###.###}", cSort, nTotal, nEnd / 1000)

    End Sub

    Sub Swap(ByVal nPos1 As Integer, ByVal nPos2 As Integer)

        Dim cTemp

        cTemp = Me.arData(nPos1)

        Me.arData(nPos1) = Me.arData(nPos2)

        Me.arData(nPos2) = cTemp

        ShowChar(Me.arData(nPos1), nPos1)

        ShowChar(Me.ardata(nPos2), nPos2)

    End Sub

    Sub BubbleSort(ByVal nStart As Integer, ByVal nMax As Integer)

        Dim i, j As Integer

        For i = 0 To nMax

            For j = 0 To i

                If Me.arData(i) < Me.arData(j) Then

                    Swap(i, j)

                End If

         Next

        Next

    End Sub

    Sub InsertionSort(ByVal nStart As Integer, ByVal nMax As Integer)

        Dim i, j As Integer

        Dim t As Char

        For j = 1 To nMax

            If Me.arData(j - 1) > Me.arData(j) Then ' compare adjacent elements

                t = Me.arData(j)

                For i = j To 1 Step -1 ' shift the rest down

                    Me.arData(i) = Me.arData(i - 1)

                    Me.ShowChar(Me.arData(i), i)

                    If Me.arData(i - 1) < t Then

                        Exit For

                    End If

                Next

                Me.arData(i) = t

                Me.ShowChar(t, i)

            End If

        Next

    End Sub

    Sub ShellSort(ByVal nStart As Integer, ByVal nMax As Integer)

        Dim g, i, j As Integer

        g = Int(nMax / 2)

        Do While g > 0

            For i = g To nMax

                j = i - g

                Do While j >= 0 AndAlso Me.arData(j) > Me.arData(j + g)

                    Me.Swap(j, j + g)

                    j = j - g ' next group

                    If j < 0 Then

                        'Exit Do

                    End If

                Loop

            Next

            g = Int(g / 2)

        Loop

    End Sub

    Sub QuickSort(ByVal nLeft As Integer, ByVal nRight As Integer)

        Dim cKey As Char, i, j As Integer

        If nLeft >= nRight Then ' if the pointers cross, then we're done

            Return

        End If

        cKey = Me.arData(nLeft)

        i = nLeft ' start the left and right index pointers

        j = nRight + 1

        Do While j > i ' as the poitners move toward each other without crossing

            i = i + 1

            Do While Me.arData(i) < cKey 'move the left pointer til we find one out of pos

                i += 1

            Loop

            j -= 1

            Do While Me.arData(j) > cKey 'move the right pointer til we find one out of pos

                j -= 1

            Loop

            If j > i Then

            Me.Swap(j, i)

            End If

        Loop

        Me.Swap(j, nLeft) 'now we know the key goes into position nleft

        Me.QuickSort(nLeft, j - 1) ' sort left & right sides

        Me.QuickSort(j + 1, nRight)

    End Sub

    Sub SuperSort(ByVal nStart As Integer, ByVal nMax As Integer)

        'What 14 lines of superfast code should go here to accomplish the task of sorting all the data?

End Class

Comments

  • Anonymous
    September 19, 2006
    Calvin,
    A VFP Specific Solution in 12 lines (including Endproc???

    Procedure SuperSort(nStart,nMax)
    local I
      create cursor curSort(ID C(1))
      for I=nStart to nMax
         Insert into curSort values (This.arData[I])
      endfor
      index on Id tag Id
     
      I=1
      scan
         @ Int((I-1)/This.nCols), Int((I-1)%This.nCols) Say curSort->Id
         I=I+1
      endscan
    endproc

    Dave Crozier
    All in 12 Lines!
  • Anonymous
    September 19, 2006
    The comment has been removed
  • Anonymous
    September 19, 2006
    This is probably closer to what's expected...

    LOCAL ARRAY aa[26]
    LOCAL nCnt1, nCnt2, nCnt3, nPos, nMax, nWritten, nTot
    STORE 0 TO m.nPos
    FOR m.nCnt1 = 1 TO m.nMax
    m.nCnt2 = 1 + ASC(this.ardata[m.nCnt1])-ASC("A")
    m.aa[m.nCnt2]=1+IIF(VARTYPE(aa[m.nCnt2])="L",0,m.aa[m.nCnt2])
    ENDFOR
    FOR m.nCnt3 = 0 TO 25
    m.nTot = m.aa[m.nCnt3+1]
    FOR m.nWritten = 1 TO m.nTot
    @ INT((m.nPos+m.nWritten-1)/this.nCols), INT((m.nPos+m.nWritten-1)%this.nCols) say CHR(m.nCnt3+ASC("A"))
    ENDFOR
    m.nPos = m.nPos  + m.nTot
    ENDFOR