• 🏆 Texturing Contest #33 is OPEN! Contestants must re-texture a SD unit model found in-game (Warcraft 3 Classic), recreating the unit into a peaceful NPC version. 🔗Click here to enter!
  • It's time for the first HD Modeling Contest of 2024. Join the theme discussion for Hive's HD Modeling Contest #6! Click here to post your idea!

[JASS] QuickSort

Normal sorting only sorts values from low to high or vice versa.
With index sorting you may attach your struct instance as index for example.

Normal sorting
JASS:
library QuickSort

/*
    Allows to sort an array of integer/real.
    Complexity:
        Best: O(n*log (n))
        Average: O(n log n) .
        Worst: O(n^2)
       
    // For bigger unsorted arrays it gets unlikely to get the worst case.
    // In tests it could sort arrays with size of ~650 until OP limit was hit.
   

   Usage
  ¯¯¯¯¯¯¯
*/
//! novjass
    // Fill this array:
            real array QuickSortValue[]
   
    // Ascending:
            boolean QuickSortAscending
                    // just set it to true or false
   
    // Start:
            function QuickSort takes integer left, integer right returns nothing
                    // left is index where it starts
                    // right is index where it ends
           
//==============================================================//
//! endnovjass
    globals
        real array QuickSortValue
        integer array QuickSortIndix
        boolean QuickSortAscending = true
    endglobals
   
/*
    Get median of three values.

     if (i < j)then
        if(j<k) then
            return j
        elseif (k<pivot) then
            return = k
        else
            return i
        endif
    elseif (k<j) then
        return j
    elseif (k<i) then
        return k
    else
        return i
    endif
*/
   
   
    private function Partition takes integer left, integer right returns integer
        local real pivot = QuickSortValue[left]
        local integer l = left
        local integer r = right
        local real temp
        loop
            exitwhen l > r
            if (QuickSortAscending and QuickSortValue[l] > pivot) or (not QuickSortAscending and QuickSortValue[l] < pivot) then
                set temp = QuickSortValue[l]
                set QuickSortValue[l] = QuickSortValue[r]
                set QuickSortValue[r] = temp
                set r = r - 1
            else
                set l = l + 1
            endif
        endloop
        set temp = QuickSortValue[left]
        set QuickSortValue[left] = QuickSortValue[r]
        set QuickSortValue[r] = temp
        return r
    endfunction
   
    function QuickSort takes integer left, integer right returns nothing
        local integer i
        if (left < right) then
            set i = Partition(left, right)
            call QuickSort(left, i - 1)
            call QuickSort(i + 1, right)
        endif
    endfunction

endlibrary

Index sorting
JASS:
library QuickSort

/*
    Allows to sort an array of integer/real.
    Binds an extra integer (index)
    to the filled value to allow keeping track of it.
   
    Complexity:
        Best: O(n*log (n))
        Average: O(n log n) .
        Worst: O(n^2)
       
    // For bigger unsorted arrays it gets unlikely to get the worst case.
    // In tests it could sort arrays with size of ~650 until OP limit was hit.
   

   Usage
  ¯¯¯¯¯¯¯
*/
//! novjass
    // Fill these arrays:
            real    array QuickSortValue[]
            integer array QuickSortIndex[]  // Requierement, not optional.
   
    // Ascending:
            boolean QuickSortAscending
                    // just set it to true or false
   
    // Start:
            function QuickSort takes integer left, integer right returns nothing
                    // left is index where it starts
                    // right is index where it ends
           
//==============================================================//
//! endnovjass
    globals
        real array QuickSortValue
        integer array QuickSortIndex
        boolean QuickSortAscending = true
    endglobals
   
/*
    Get median of three values.

     if (i < j)then
        if(j<k) then
            return j
        elseif (k<pivot) then
            return = k
        else
            return i
        endif
    elseif (k<j) then
        return j
    elseif (k<i) then
        return k
    else
        return i
    endif
*/
    private function Partition takes integer left, integer right returns integer
        local real pivot = QuickSortValue[left]
        local integer l = left
        local integer r = right
        local real tempR
        local integer tempI
        loop
            exitwhen l > r
            if (QuickSortAscending and QuickSortValue[l] > pivot) or (not QuickSortAscending and QuickSortValue[l] < pivot) then
                set tempR = QuickSortValue[l]
                set QuickSortValue[l] = QuickSortValue[r]
                set QuickSortValue[r] = tempR
                set tempI = QuickSortIndex[l]
                set QuickSortIndex[l] = QuickSortIndex[r]
                set QuickSortIndex[r] = tempI
                set r = r - 1
            else
                set l = l + 1
            endif
        endloop
        set tempR = QuickSortValue[left]
        set QuickSortValue[left] = QuickSortValue[r]
        set QuickSortValue[r] = tempR
        set tempI = QuickSortIndex[left]
        set QuickSortIndex[left] = QuickSortIndex[r]
        set QuickSortIndex[r] = tempI
        return r
    endfunction
   
    function QuickSort takes integer left, integer right returns nothing
        local integer i
        if (left < right) then
            set i = Partition(left, right)
            call QuickSort(left, i - 1)
            call QuickSort(i + 1, right)
        endif
    endfunction
endlibrary

JASS:
scope QSortDemoOne initializer Init

    private function print takes integer i, integer j returns nothing
        loop
            exitwhen i > j
            call BJDebugMsg(R2S(QuickSortValue[i]))
            set i = i + 1
        endloop
    endfunction

   private function Init takes nothing returns nothing
       
        set QuickSortValue[0] = 5.222
        set QuickSortValue[1] = 5.111
        set QuickSortValue[2] = 5.222
        set QuickSortValue[3] = 5.555
        set QuickSortValue[4] = 5.777
        set QuickSortValue[5] = -5.111
        set QuickSortValue[6] = -5
        set QuickSortValue[7] = 0
       
        set QuickSortAscending = false
        call QuickSort(0, 7)
        call print(0, 7)
       
    endfunction
endscope

JASS:
scope QSortDemoTwo
private struct QSortDemo extends array
       
    private real value
       
    private static integer count = 5
    private static integer max = 0
    private static method create takes real val returns thistype
        set max = max + 1
        set thistype(max).value = val
        return max
    endmethod
       
    private static method callback takes nothing returns nothing
        local integer i = 1
        call BJDebugMsg("==== Sorted ====")
        call QuickSort(1, max)
        loop
            exitwhen i > count
            call BJDebugMsg(I2S(thistype(QuickSortIndex[i])) + " : " + R2S(thistype(QuickSortIndex[i]).value))
            set i = i + 1
        endloop
    endmethod
       
    private static method onInit takes nothing returns nothing
        local integer i = 1
        local thistype this
        call BJDebugMsg("==== Unsorted ====")
        loop
            exitwhen i > count
            set this = thistype.create(GetRandomReal(0, 100))
            set QuickSortValue[i] = this.value
            set QuickSortIndex[i] = this
            call BJDebugMsg(I2S(thistype(i)) + " : " + R2S(thistype(i).value))
            set i = i + 1
        endloop
        call TimerStart(CreateTimer(), 0, false, function thistype.callback)
    endmethod
endstruct
endscope

Sorters hate that trick
 
Last edited:
That would be a nice average. :D
You were actually motivating me to submit it, but it needed a bit of rewrote.

Maybe yes, but I'm uncertain if I directly should go with complex data types (would look uglier), or with indices only.
http://www.hiveworkshop.com/forums/jass-resources-412/snippet-mergesort-206213/
^But on other side, as something like this doex exist, maybe just a plain integer/real array sorter as stand alone is also nothing bad.
 
Level 22
Joined
Feb 6, 2014
Messages
2,466
Sort Comparison



JASS:
library BubbleSort
    
    globals
        integer array BubbleSortValues
        boolean BubbleSortAscending = true
    endglobals
    
    function DisplayBubbleSortValues takes integer start, integer end returns nothing
        local integer i = start
        loop
            exitwhen i > end
            call BJDebugMsg("BubbleSortValues[" + I2S(i) + "] = " + I2S(BubbleSortValues[i]))
            set i = i + 1
        endloop
    endfunction
    
    function BubbleSort takes integer start, integer end returns nothing
        local integer k = end - start
        local integer i
        local integer j
        local integer temp
        loop
            exitwhen k == 0
            set i = start
            set j = i + 1
            loop
                exitwhen j > end
                if BubbleSortAscending then
                    if BubbleSortValues[j] < BubbleSortValues[i] then
                        set temp = BubbleSortValues[i]
                        set BubbleSortValues[i] = BubbleSortValues[j]
                        set BubbleSortValues[j] = temp
                    endif
                else
                    if BubbleSortValues[j] > BubbleSortValues[i] then
                        set temp = BubbleSortValues[i]
                        set BubbleSortValues[i] = BubbleSortValues[j]
                        set BubbleSortValues[j] = temp
                    endif
                endif
                set i = i + 1
                set j = j + 1
            endloop
            set k = k - 1
        endloop
    endfunction

endlibrary



JASS:
library MergeSort

    
    globals
        integer array MergeSortValues
        boolean MergeSortAscending = true
        
        private integer array left
        private integer array right
    endglobals
    
    function DisplayMergeSortValues takes integer start, integer end returns nothing
        local integer i = start
        loop
            exitwhen i > end
            call BJDebugMsg("MergeSortValues[" + I2S(i) + "] = " + I2S(MergeSortValues[i]))
            set i = i + 1
        endloop
    endfunction
    
    private function Merge takes integer leftStart, integer leftEnd, integer rightStart, integer rightEnd returns nothing
        local integer i = leftStart
        local integer j = rightStart
        local integer k = i
        loop
            exitwhen i > leftEnd or j > rightEnd
            if MergeSortAscending then
                if left[i] < right[j] then
                    set MergeSortValues[k] = left[i]
                    set i = i + 1
                    set k = k + 1
                else
                    set MergeSortValues[k] = right[j]
                    set j = j + 1
                    set k = k + 1
                endif
            else
                if left[i] > right[j] then
                    set MergeSortValues[k] = left[i]
                    set i = i + 1
                    set k = k + 1
                else
                    set MergeSortValues[k] = right[j]
                    set j = j + 1
                    set k = k + 1
                endif
            endif
        endloop
        loop
            exitwhen i > leftEnd
            set MergeSortValues[k] = left[i]
            set k = k + 1
            set i = i + 1
        endloop
        loop
            exitwhen j > rightEnd
            set MergeSortValues[k] = right[j]
            set k = k + 1
            set j = j + 1
        endloop
    endfunction
    
    function MergeSort takes integer start, integer end returns nothing
        local integer i = start
        local integer mid
        if end - start > 0 then
            set mid = (start + end)/2
            call MergeSort(start, mid)
            call MergeSort(mid+1, end)
            loop
                exitwhen i > mid
                set left[i] = MergeSortValues[i]
                set i = i + 1
            endloop
            loop
                exitwhen i > end
                set right[i] = MergeSortValues[i]
                set i = i + 1
            endloop
            call Merge(start, mid, mid+1, end)
        endif
    endfunction

endlibrary



JASS:
library QuickSort

/*
    Allows to sort an array of integer/real.
    Complexity:
        Best: O(n*log (n))
        Average: O(n log n) .
        Worst: O(n^2)
        
    // For bigger unsorted arrays it gets unlikely to get the worst case.
    // In tests it could sort arrays with size of ~650 until OP limit was hit.
    

   Usage
  ¯¯¯¯¯¯¯
*/
//! novjass
    // Fill this array:
            real array QuickSortArray[]
    
    // Ascending:
            boolean QuickSortAscending
                    // just set it to true or false
    
    // Start:
            function QuickSort takes integer left, integer right returns nothing
                    // left is index where it starts
                    // right is index where it ends
            
//==============================================================//
//! endnovjass
    globals
        real array QuickSortArray
        boolean QuickSortAscending = true
        private real temp
    endglobals
    
    private function Partition takes integer left, integer right returns integer
        local real pivot = QuickSortArray[left]
        local integer l = left
        local integer r = right
        loop
            exitwhen l > r
            if (QuickSortAscending and QuickSortArray[l] > pivot) or (not QuickSortAscending and QuickSortArray[l] < pivot) then
                set temp = QuickSortArray[l]
                set QuickSortArray[l] = QuickSortArray[r]
                set QuickSortArray[r] = temp
                set r = r - 1
            else
                set l = l + 1
            endif
        endloop
        set temp = QuickSortArray[left]
        set QuickSortArray[left] = QuickSortArray[r]
        set QuickSortArray[r] = temp
        return r
    endfunction
    
    function QuickSort takes integer left, integer right returns nothing
        local integer i
        if (left < right) then
            set i = Partition(left, right)
            call QuickSort(left, i - 1)
            call QuickSort(i + 1, right)
        endif
    endfunction
endlibrary


JASS:
library InsertionSort
    
    globals
        integer array InsertSortValues
        boolean InsertSortAscending = true
    endglobals
    
    function DisplayInsertSortValues takes integer start, integer end returns nothing
        local integer i = start
        loop
            exitwhen i > end
            call BJDebugMsg("InsertSortValues[" + I2S(i) + "] = " + I2S(InsertSortValues[i]))
            set i = i + 1
        endloop
    endfunction
    
    function InsertionSort takes integer start, integer end returns nothing
        local integer i = start
        local integer j
        local integer length
        local integer swap
        loop
            exitwhen i > end
            set swap = InsertSortValues[i]
            set j = i - 1
            loop
                exitwhen j < 0 or InsertSortValues[j] <= swap // ASC, DESC: A[j] >= swap
                set InsertSortValues[j + 1] = InsertSortValues[j]
                set j = j - 1
            endloop
            set InsertSortValues[j + 1] = swap
            set i = i + 1
        endloop
    endfunction

endlibrary


JASS:
scope Test initializer OnInit
    
    
    //Test Condition:
    //Array values are random each test
    
    globals
        private integer count = 70
        
        private constant integer RANDOM_VALUES = 1
        private constant integer SORTED_ASCENDING = 2
        private constant integer SORTED_DESCENDING = 3
        
        //Configure Test Condition Here
        private constant integer TEST_CONDITION = RANDOM_VALUES
        
        private trigger newValues
        private trigger bubbleSort
        private trigger mergeSort
        private trigger quickSort
        private trigger insertSort
    endglobals
    
    private function NewValues takes nothing returns boolean
        local integer i = 0
        local integer value
        loop
            exitwhen i > count
            if TEST_CONDITION == RANDOM_VALUES then
                set value = GetRandomInt(-9999, 9999)
            elseif TEST_CONDITION == SORTED_ASCENDING then
                set value = i
            elseif TEST_CONDITION == SORTED_DESCENDING then
                set value = count - i
            endif
            set BubbleSortValues[i] = value
            set MergeSortValues[i] = value
            set QuickSortArray[i] = value
            set InsertSortValues[i] = value
            set i = i + 1
        endloop
        call BJDebugMsg("|cffffcc00Finished Assigning New Similar Values|r")
        return false
    endfunction
    
    private function RunBubbleSort takes nothing returns boolean
        call BJDebugMsg("BubbleSort starting")
        call BubbleSort(0, count)
        call BJDebugMsg("|cff00cc00BubbleSort finished|r")
        //call DisplayBubbleSortValues(0, count)
        return false
    endfunction
    
    private function RunMergeSort takes nothing returns boolean
        call BJDebugMsg("MergeSort starting")
        call MergeSort(0, count)
        call BJDebugMsg("|cff00cc00MergeSort finished|r")
        //call DisplayMergeSortValues(0, count)
        return false
    endfunction
    
    private function RunQuickSort takes nothing returns boolean
        call BJDebugMsg("QuickSort starting")
        call QuickSort(0, count)
        call BJDebugMsg("|cff00cc00QuickSort finished|r")
        return false
    endfunction
    
    private function RunInsertSort takes nothing returns boolean
        call BJDebugMsg("InsertionSort starting")
        call InsertionSort(0, count)
        call BJDebugMsg("|cff00cc00InsertionSort finished|r")
        //call DisplayInsertSortValues(0, count)
        return false
    endfunction
    
    private function IncreaseCount takes nothing returns nothing
        call TriggerEvaluate(newValues)
        call BJDebugMsg("Count = |cffffcc00" + I2S(count) + "|r")
        call TriggerEvaluate(quickSort)
        call TriggerEvaluate(mergeSort)
        //call TriggerEvaluate(bubbleSort)
        //call TriggerEvaluate(insertSort)
        set count = count + 1
    endfunction
    
    private function OnInit takes nothing returns nothing
        local integer i = 0
        local integer value
        call TimerStart(CreateTimer(), 0.2, true, function IncreaseCount)
        set newValues = CreateTrigger()
        set bubbleSort = CreateTrigger()
        set mergeSort = CreateTrigger()
        set quickSort = CreateTrigger()
        set insertSort = CreateTrigger()
        call TriggerAddCondition(newValues, function NewValues)
        call TriggerAddCondition(bubbleSort, function RunBubbleSort)
        call TriggerAddCondition(mergeSort, function RunMergeSort)
        call TriggerAddCondition(quickSort, function RunQuickSort)
        call TriggerAddCondition(insertSort, function RunInsertSort)
    endfunction

endscope




Disclaimer: Test results may vary

List to be sorted has random values

Bubble Sort
n = 93

Insertion Sort
n = 184

Merge Sort
n= 452

Quick Sort
n = 617




List to be sorted is already sorted in ascending order

Bubble Sort
n = 99

Insertion Sort
n > 1000

Merge Sort
n = 488

Quick Sort
n = 306




List to be sorted is already sorted in descending order

Bubble Sort
n = 89

Insertion Sort
n = 136

Merge Sort
n = 488

Quick Sort
n = 152





Conclusion:

Quick Sort:
- Very good sorting capability, highly efficient on larger random lists. Perform worse if the list to be sorted is already sorted or reverse sorted.

Merge Sort:
- Consistently good sorting capability on all cases, efficient on larger lists.

Insertion Sort:
- Far better than BubbleSort, but less efficient on larger lists, or when the list to be sorted is sorted in reverse. Performs best if the list to be sorted is already sorted.

Bubble Sort:
- Bad sorting capability on all cases.
 

Attachments

  • Sort Test.w3m
    20.3 KB · Views: 103
Last edited:
Level 13
Joined
Nov 7, 2014
Messages
571
I got pretty much the same values

quick sort: ~600 for random values, you need to be lucky with the pivot to get more
merge sort: always 447, which is awesome
bubble sort: always 91 which is not so awesome
inline insertion sort: ~185 for random values (much better for not so random), which is kind of bad but it's short (in lines of code) and can sort any type of array (because you can inline it easily :p)

inline insertion sort template
JASS:
local <type> array A // 0 based
local <type> swap
local integer i
local integer j

set i = 1
loop
    exitwhen i >= <A.length>
    set swap = A[i]
    set j = i - 1
    loop
        exitwhen j < 0 or A[j] <= swap // ASC, DESC: A[j] >= swap
        set A[j + 1] = A[j]
        set j = j - 1
    endloop
    set A[j + 1] = swap
    set i = i + 1
endloop
 
Nice results you made there, Flux. Thanks!
So it seems we mimiced it correctly. Msort is stable and good. QSort might be a bit better in cases, but unstable.

Aniki, if you wrote it, we could put it together later in the collection.
Might be cool if you have one from each. (or someone else)

I never mentioned before that JASS tags have "Collapse" and "Expand" buttons, lol.
 

LeP

LeP

Level 13
Joined
Feb 13, 2008
Messages
539
In my tests hybrid sort outperformes all of these (which makes sense). I changed the interface so that it works exactly like all these other sorts posted in here without textmacros and structs.
It's up to everybody else to decide which interface is better for them.

On the other hand i'm not sure if i ever needed to sort values in jass (maybe once?). I think most of the time you can build up a sorted datastructure incrementally.
 
Level 13
Joined
Nov 7, 2014
Messages
571
For the "lulz" it's probably worth mentioning sorting networks, which can sort fixed sized arrays. This page can generate sorting networks of different sizes implemented with SWAP macros.

An example for an array of size 12 (bj_MAX_PLAYERS):

JASS:
    local integer array A
    local integer i

    call BJDebugMsg("UNSORTED:")

    set i = 0
    loop
        exitwhen i >= 12
        set A[i] = GetRandomInt(1, 100)
        call BJDebugMsg(I2S(A[i]))
        set i = i + 1
    endloop

    call BJDebugMsg("SORTED:")

    //! textmacro SWAP takes ARRAY, A, B
    if $ARRAY$[$A$] > $ARRAY$[$B$] then
        set bj_forLoopAIndex = $ARRAY$[$A$]
        set $ARRAY$[$A$] = $ARRAY$[$B$]
        set $ARRAY$[$B$] = bj_forLoopAIndex
    endif
    //! endtextmacro

    //! runtextmacro SWAP("A", "1", "2")
    //! runtextmacro SWAP("A", "0", "2")
    //! runtextmacro SWAP("A", "0", "1")
    //! runtextmacro SWAP("A", "4", "5")
    //! runtextmacro SWAP("A", "3", "5")
    //! runtextmacro SWAP("A", "3", "4")
    //! runtextmacro SWAP("A", "0", "3")
    //! runtextmacro SWAP("A", "1", "4")
    //! runtextmacro SWAP("A", "2", "5")
    //! runtextmacro SWAP("A", "2", "4")
    //! runtextmacro SWAP("A", "1", "3")
    //! runtextmacro SWAP("A", "2", "3")
    //! runtextmacro SWAP("A", "7", "8")
    //! runtextmacro SWAP("A", "6", "8")
    //! runtextmacro SWAP("A", "6", "7")
    //! runtextmacro SWAP("A", "10", "11")
    //! runtextmacro SWAP("A", "9", "11")
    //! runtextmacro SWAP("A", "9", "10")
    //! runtextmacro SWAP("A", "6", "9")
    //! runtextmacro SWAP("A", "7", "10")
    //! runtextmacro SWAP("A", "8", "11")
    //! runtextmacro SWAP("A", "8", "10")
    //! runtextmacro SWAP("A", "7", "9")
    //! runtextmacro SWAP("A", "8", "9")
    //! runtextmacro SWAP("A", "0", "6")
    //! runtextmacro SWAP("A", "1", "7")
    //! runtextmacro SWAP("A", "2", "8")
    //! runtextmacro SWAP("A", "2", "7")
    //! runtextmacro SWAP("A", "1", "6")
    //! runtextmacro SWAP("A", "2", "6")
    //! runtextmacro SWAP("A", "3", "9")
    //! runtextmacro SWAP("A", "4", "10")
    //! runtextmacro SWAP("A", "5", "11")
    //! runtextmacro SWAP("A", "5", "10")
    //! runtextmacro SWAP("A", "4", "9")
    //! runtextmacro SWAP("A", "5", "9")
    //! runtextmacro SWAP("A", "3", "6")
    //! runtextmacro SWAP("A", "4", "7")
    //! runtextmacro SWAP("A", "5", "8")
    //! runtextmacro SWAP("A", "5", "7")
    //! runtextmacro SWAP("A", "4", "6")
    //! runtextmacro SWAP("A", "5", "6")

    set i = 0
    loop
        exitwhen i >= 12
        call BJDebugMsg(I2S(A[i]))
        set i = i + 1
    endloop
 
Level 14
Joined
Dec 12, 2012
Messages
1,007
A standardized sorting library would be nice I think. But the array type as well as the comparator should be customizable by the user. Something like:

JASS:
function RealPredicate takes real r1, real r2 returns boolean
    return r1 < r2 // We sort ascending
endfunction

//! runtextmacro CREATE_QUICK_SORT("real", "RealPredicate")

to generate the desired sorting code.
 
Top