Author Topic: Recursive Vrs NonRecursive test with Quicksort  (Read 5046 times)

0 Members and 1 Guest are viewing this topic.

Offline bplus

  • Global Moderator
  • Forum Resident
  • Posts: 8053
  • b = b + ...
    • View Profile
Recursive Vrs NonRecursive test with Quicksort
« on: November 01, 2021, 08:29:10 am »
Seeing Sanmayce Quicksort test with my recursive Quicksort I had to try my own test without using the array in the parameters/arguments call, plus in the Discussion Board Recursion topic brought up some ideas from Luke on recursion that I've been curious experimenting with for some time.

So here's my quick test:
Code: QB64: [Select]
  1. _Title "Recursive Vrs NonRecursive test with Quicksort" 'b+ 2021-11-01
  2. ArrayHigh = 100000000
  3. ReDim Shared As _Unsigned _Integer64 n(1 To ArrayHigh), m(1 To ArrayHigh)
  4.  
  5. For i = 1 To ArrayHigh
  6.     n(i) = i
  7. For i = ArrayHigh To 2 Step -1 ' Fisher Yates Shuffle
  8.     Swap n(i), n(Int(Rnd * i) + 1)
  9.     m(i) = n(i) ' copy m off n
  10. Print "Test arrays ready. Testing Recursive QuickSort first."
  11. m(1) = n(1)
  12. T# = Timer(.001)
  13. QuickSort 1, ArrayHigh
  14. QuickSortT# = Timer(.001) - T#
  15. For i = 1 To ArrayHigh
  16.     If n(i) <> i Then Beep: Print "Error in QuickSort array n."
  17. Print "QuickSort checked, QuickSort time was"; QuickSortT#
  18.  
  19. T# = Timer(.001)
  20. Quicksort_QB64
  21. QuicksortQB64T# = Timer(.001) - T#
  22. For i = 1 To ArrayHigh
  23.     If m(i) <> i Then Beep: Print "Error in Quicksort__QB64 array m."
  24. Print "Quicksort_QB64 checked, Quicksort_QB64 time was"; QuicksortQB64T#
  25.  
  26. ' recursive
  27. Sub QuickSort (start As _Unsigned _Integer64, finish As _Unsigned _Integer64)
  28.     Lo = start: Hi = finish
  29.     Middle = n((Lo + Hi) \ 2)
  30.     Do
  31.         Do While n(Lo) < Middle: Lo = Lo + 1: Loop
  32.         Do While n(Hi) > Middle: Hi = Hi - 1: Loop
  33.         If Lo <= Hi Then
  34.             Swap n(Lo), n(Hi)
  35.             Lo = Lo + 1: Hi = Hi - 1
  36.         End If
  37.     Loop Until Lo > Hi
  38.     If Hi > start Then QuickSort start, Hi
  39.     If Lo < finish Then QuickSort Lo, finish
  40.  
  41. ' non recursive
  42. ' ref:  https://rosettacode.org/wiki/Sorting_algorithms/Quicksort#QB64
  43. Sub Quicksort_QB64
  44.     Left = LBound(m)
  45.     Right = UBound(m)
  46.     LeftMargin = Left
  47.     ReDim Stack&&(Left To Right)
  48.     StackPtr = 0
  49.     StackPtr = StackPtr + 1
  50.     Stack&&(StackPtr + LeftMargin) = Left
  51.     StackPtr = StackPtr + 1
  52.     Stack&&(StackPtr + LeftMargin) = Right
  53.     Do 'Until StackPtr = 0
  54.         Right = Stack&&(StackPtr + LeftMargin)
  55.         StackPtr = StackPtr - 1
  56.         Left = Stack&&(StackPtr + LeftMargin)
  57.         StackPtr = StackPtr - 1
  58.         Do 'Until Left >= Right
  59.             Pivot~&& = m((Left + Right) \ 2)
  60.             Indx = Left
  61.             Jndx = Right
  62.             Do
  63.                 Do While (m(Indx) < Pivot~&&)
  64.                     Indx = Indx + 1
  65.                 Loop
  66.                 Do While (m(Jndx) > Pivot~&&)
  67.                     Jndx = Jndx - 1
  68.                 Loop
  69.                 If Indx <= Jndx Then
  70.                     If Indx < Jndx Then Swap m(Indx), m(Jndx)
  71.                     Indx = Indx + 1
  72.                     Jndx = Jndx - 1
  73.                 End If
  74.             Loop While Indx <= Jndx
  75.             If Indx < Right Then
  76.                 StackPtr = StackPtr + 1
  77.                 Stack&&(StackPtr + LeftMargin) = Indx
  78.                 StackPtr = StackPtr + 1
  79.                 Stack&&(StackPtr + LeftMargin) = Right
  80.             End If
  81.             Right = Jndx
  82.         Loop Until Left >= Right
  83.     Loop Until StackPtr = 0
  84.  
  85.  

Nice non recursion code, doing it with your own stack is interesting. I don't suppose there's a way to do a recursive quicksort without need of any parameters to call? Hmm...


EDIT: Dang it! I never use Call this is what I get for copying what I thought was my code from someone else post. :(
EDIT: Fix a labeling if error found in 2nd test.
« Last Edit: November 01, 2021, 10:55:54 am by bplus »

Offline jack

  • Seasoned Forum Regular
  • Posts: 408
    • View Profile
Re: Recursive Vrs NonRecursive test with Quicksort
« Reply #1 on: November 01, 2021, 11:36:48 am »
bplus, I don't quite agree with your definition of non-recursive
looks like a dirty hack and not a real non-recursive quick-sort, don't know that it's even possible

Offline bplus

  • Global Moderator
  • Forum Resident
  • Posts: 8053
  • b = b + ...
    • View Profile
Re: Recursive Vrs NonRecursive test with Quicksort
« Reply #2 on: November 01, 2021, 12:04:01 pm »
bplus, I don't quite agree with your definition of non-recursive
looks like a dirty hack and not a real non-recursive quick-sort, don't know that it's even possible

Hi Jack,

My definition of non recursive subroutine is one that doesn't call itself to do the job. That draws the line pretty clearly to me.

What may be in question is if the non recursive routine (Sanmayce's post at Rosetta Code) is truly in spirit of Quicksort algorithm and I think it works exactly the same using pivots and working left and right sections of pivot with 2 indexes.

What doesn't look real to you? The stack and it's pointer?

Offline jack

  • Seasoned Forum Regular
  • Posts: 408
    • View Profile
Re: Recursive Vrs NonRecursive test with Quicksort
« Reply #3 on: November 01, 2021, 12:31:49 pm »
bplus, you are basically simulating recursion not replacing it

Offline Dimster

  • Forum Resident
  • Posts: 500
    • View Profile
Re: Recursive Vrs NonRecursive test with Quicksort
« Reply #4 on: November 01, 2021, 12:32:17 pm »
Hi as well Jack - It does seem natural that recursive means multiple iteration to the same code. So I was looking at it as perhaps a loop in the main program invoking a sub/function multiple times. But as I'm beginning to learn, recursive means a routine CALLING itself. Steve just provided a recursive routine using IF statements which I'm still trying to make fit into the new definition of recursiveness that I'm formulating. I would be interested in what "Recursive" ( in terms of our use of QB64 coding) means to you.

Offline bplus

  • Global Moderator
  • Forum Resident
  • Posts: 8053
  • b = b + ...
    • View Profile
Re: Recursive Vrs NonRecursive test with Quicksort
« Reply #5 on: November 01, 2021, 01:10:01 pm »
Looking at Wiki article on Quicksort, it is described as recursive. But does that mean it's not a Quicksort routine if not done recursively? I think not.

What I'd really like to see is if anyone can do Quicksort recursively without parameters in the call to itself!

Jack described the non recursive routine as ugly, I agree, recursive routines are much more elegant (unless you try to hack a no parameters routine for Quicksort LOL).

Offline TempodiBasic

  • Forum Resident
  • Posts: 1792
    • View Profile
Re: Recursive Vrs NonRecursive test with Quicksort
« Reply #6 on: November 01, 2021, 05:40:06 pm »
Hi dear friends

about Quicksort
starting with a unordered finished set of homogeneus values
to get the goal to order this set
 1. pickup a cutoff value
 2. from its left move to right (over) the greater values than it
 3. from its right move to left (under) the smaller values than it
 4. do again this work for subset left and right of this pivot (here it can entry the recursive method in alternative to iterative method)

-------------
about Iterative quicksort routine posted on Rosetta Code  https://rosettacode.org/wiki/Sorting_algorithms/Quicksort#QB64
it uses confounding name of variables but surely it is not an emulation of stack, moreover to be iterative you must keep trace of your pivot values and Left values and Right values to be sure to run code UNTIL all subsets >1 are evaluated and ordered.

So surely in the iterative way you must write the code to keep value of pivot and left margin and right margin around the loop of a single run of Quicksort (the point n°4 of the overtype scheme)
the best choice seems to be the tail that is a LIFO structure.

here some web informations
https://stackoverflow.com/questions/12553238/quicksort-iterative-or-recursive
https://www.techiedelight.com/iterative-implementation-of-quicksort/
https://www.geeksforgeeks.org/iterative-quick-sort/?ref=lbp

       Happy study
Programming isn't difficult, only it's  consuming time and coffee

Offline bplus

  • Global Moderator
  • Forum Resident
  • Posts: 8053
  • b = b + ...
    • View Profile
Re: Recursive Vrs NonRecursive test with Quicksort
« Reply #7 on: November 01, 2021, 06:56:46 pm »
I don't know if you guys are understanding what I am doing here. It is just a speed test between a Quicksort recursive subroutine versus a very nice non recursive Quicksort routine that does the very same thing only slightly faster and it does it by emulating how a recursive subroutine works with internal stack by providing a sort (no pun intended) of pseudo stack in the non recursive routine.

Offline bplus

  • Global Moderator
  • Forum Resident
  • Posts: 8053
  • b = b + ...
    • View Profile
Re: Recursive Vrs NonRecursive test with Quicksort
« Reply #8 on: November 01, 2021, 10:06:33 pm »
I did it, created recursive Quicksort routine without parameters! I used a Shared Stack array and Stack pointer. It is the third test added to the other 2:
Code: QB64: [Select]
  1. _Title "Recursive Vrs NonRecursive #2 test with Quicksort" 'b+ 2021-11-01
  2. '2021-11-01 #2 test try a recursive sub wo parameters
  3.  
  4. ReDim Shared As _Unsigned _Integer64 ArrayHigh, StackPt
  5. ArrayHigh = 10000000
  6. ReDim Shared n(1 To ArrayHigh), m(1 To ArrayHigh), o(1 To ArrayHigh)
  7. ReDim Shared Stack(0 To ArrayHigh \ 8)
  8.  
  9. For i = 1 To ArrayHigh
  10.     n(i) = i
  11. For i = ArrayHigh To 2 Step -1 ' Fisher Yates Shuffle
  12.     Swap n(i), n(Int(Rnd * i) + 1)
  13.     m(i) = n(i) ' copy m off n
  14.     o(i) = n(i)
  15. m(1) = n(1)
  16. o(1) = n(1)
  17. Print "Test arrays ready. Testing Recursive QuickSort first."
  18.  
  19. T# = Timer(.001)
  20. QuickSort 1, ArrayHigh
  21. QuickSortT# = Timer(.001) - T#
  22. For i = 1 To ArrayHigh
  23.     If n(i) <> i Then Beep: Print "Error in QuickSort array n."
  24. Print "QuickSort checked, QuickSort time was"; QuickSortT#
  25.  
  26. T# = Timer(.001)
  27. Quicksort_QB64
  28. QuicksortQB64T# = Timer(.001) - T#
  29. For i = 1 To ArrayHigh
  30.     If m(i) <> i Then Beep: Print "Error in Quicksort__QB64 array m."
  31. Print "Quicksort_QB64 checked, Quicksort_QB64 time was"; QuicksortQB64T#
  32.  
  33. T# = Timer(.001)
  34. StackPt = StackPt + 1
  35. Stack(StackPt) = 1
  36. StackPt = StackPt + 1
  37. Stack(StackPt) = ArrayHigh
  38. QSort
  39. QSortT# = Timer(.001) - T#
  40. For i = 1 To ArrayHigh
  41.     If o(i) <> i Then Beep: Print "Error in QSort array o."
  42. Print "QSort checked, QSort time was"; QSortT#
  43.  
  44. ' recursive
  45. Sub QuickSort (start As _Unsigned _Integer64, finish As _Unsigned _Integer64)
  46.     Lo = start: Hi = finish
  47.     Middle = n((Lo + Hi) \ 2)
  48.     Do
  49.         Do While n(Lo) < Middle: Lo = Lo + 1: Loop
  50.         Do While n(Hi) > Middle: Hi = Hi - 1: Loop
  51.         If Lo <= Hi Then
  52.             Swap n(Lo), n(Hi)
  53.             Lo = Lo + 1: Hi = Hi - 1
  54.         End If
  55.     Loop Until Lo > Hi
  56.     If Hi > start Then QuickSort start, Hi
  57.     If Lo < finish Then QuickSort Lo, finish
  58.  
  59. ' non recursive
  60. ' ref:  https://rosettacode.org/wiki/Sorting_algorithms/Quicksort#QB64
  61. Sub Quicksort_QB64
  62.     Left = LBound(m)
  63.     Right = UBound(m)
  64.     LeftMargin = Left
  65.     ReDim Stack&&(Left To Right)
  66.     StackPtr = 0
  67.     StackPtr = StackPtr + 1
  68.     Stack&&(StackPtr + LeftMargin) = Left
  69.     StackPtr = StackPtr + 1
  70.     Stack&&(StackPtr + LeftMargin) = Right
  71.     Do 'Until StackPtr = 0
  72.         Right = Stack&&(StackPtr + LeftMargin)
  73.         StackPtr = StackPtr - 1
  74.         Left = Stack&&(StackPtr + LeftMargin)
  75.         StackPtr = StackPtr - 1
  76.         Do 'Until Left >= Right
  77.             Pivot~&& = m((Left + Right) \ 2)
  78.             Indx = Left
  79.             Jndx = Right
  80.             Do
  81.                 Do While (m(Indx) < Pivot~&&)
  82.                     Indx = Indx + 1
  83.                 Loop
  84.                 Do While (m(Jndx) > Pivot~&&)
  85.                     Jndx = Jndx - 1
  86.                 Loop
  87.                 If Indx <= Jndx Then
  88.                     If Indx < Jndx Then Swap m(Indx), m(Jndx)
  89.                     Indx = Indx + 1
  90.                     Jndx = Jndx - 1
  91.                 End If
  92.             Loop While Indx <= Jndx
  93.             If Indx < Right Then
  94.                 StackPtr = StackPtr + 1
  95.                 Stack&&(StackPtr + LeftMargin) = Indx
  96.                 StackPtr = StackPtr + 1
  97.                 Stack&&(StackPtr + LeftMargin) = Right
  98.             End If
  99.             Right = Jndx
  100.         Loop Until Left >= Right
  101.     Loop Until StackPtr = 0
  102.  
  103. ' recursive No Parameters!!!
  104. Sub QSort
  105.     If StackPt > 0 Then 'else done!
  106.         finish = Stack(StackPt) 'pull off last 2 arguments
  107.         StackPt = StackPt - 1
  108.         start = Stack(StackPt)
  109.         StackPt = StackPt - 1
  110.  
  111.         Lo = start: Hi = finish
  112.         Middle = o((Lo + Hi) \ 2)
  113.         Do
  114.             Do While o(Lo) < Middle: Lo = Lo + 1: Loop
  115.             Do While o(Hi) > Middle: Hi = Hi - 1: Loop
  116.             If Lo <= Hi Then
  117.                 Swap o(Lo), o(Hi)
  118.                 Lo = Lo + 1: Hi = Hi - 1
  119.             End If
  120.         Loop Until Lo > Hi
  121.  
  122.         If Hi > start Then
  123.             StackPt = StackPt + 1
  124.             Stack(StackPt) = start
  125.             StackPt = StackPt + 1
  126.             Stack(StackPt) = Hi
  127.             QSort
  128.         End If
  129.         If Lo < finish Then
  130.             StackPt = StackPt + 1
  131.             Stack(StackPt) = Lo
  132.             StackPt = StackPt + 1
  133.             Stack(StackPt) = finish
  134.             QSort
  135.         End If
  136.     End If
  137.  
  138.  
  139.  

Surprise! It turns out that it takes longer to play with a pseudo stack loading and resetting pointer than it does to call the Quicksort routine with parameters, a fairly significant amount of time longer.

Offline Sanmayce

  • Newbie
  • Posts: 63
  • Where is that English Text Sidekick?
    • View Profile
    • Sanmayce's home
Re: Recursive Vrs NonRecursive test with Quicksort
« Reply #9 on: November 04, 2021, 03:43:09 pm »
Thank you @bplus for your tests.
In the morning did write the 'Magnetica' partitioning, which helps greatly in "few distinct keys" scenario, usually 15x to 3x speed boost it gives. However, it hurts speed badly (2x) in "many distinct keys", the problem lies in STILL not well written right part traversal (Jndx), doing unnecessary swaps has to be fixed...
Your test (modified) is given below with ability (just change line 8) to run in "few distinct keys", otherwise it is the most common "many distinct keys":

 
v2_.png


"few distinct keys" scenario:

 
v2.png


Code: QB64: [Select]
  1. _Title "Recursive Vrs NonRecursive test with Quicksort" 'b+ 2021-11-01, Sanmayce added some stuff 2021-Nov-04
  2. ArrayHigh = 10000000
  3. ReDim Shared As _Unsigned _Integer64 n(1 To ArrayHigh), m(1 To ArrayHigh), z(1 To ArrayHigh)
  4.  
  5. For i = 1 To ArrayHigh
  6.     n(i) = i '13 'i
  7. For i = ArrayHigh To 2 Step -1 ' Fisher Yates Shuffle
  8.     Swap n(i), n(Int(Rnd * i) + 1)
  9.     m(i) = n(i) ' copy m off n
  10.     z(i) = n(i) ' copy m off n
  11. Print "Test arrays ready. Testing Recursive QuickSort first."
  12. n(1) = ArrayHigh
  13. m(1) = n(1)
  14. z(1) = n(1)
  15.  
  16. T# = Timer(.001)
  17. QuickSort 1, ArrayHigh
  18. QuickSortT# = Timer(.001) - T#
  19. For i = 1 To ArrayHigh - 1
  20.     If n(i) > n(i + 1) Then Print "NOT OK!": End
  21. Print "QuickSort checked, QuickSort time was"; QuickSortT#
  22.  
  23.  
  24. T# = Timer(.001)
  25. Quicksort_QB64
  26. QuicksortQB64T# = Timer(.001) - T#
  27. For i = 1 To ArrayHigh - 1
  28.     If m(i) > m(i + 1) Then Print "NOT OK!": End
  29. Print "Quicksort_QB64 checked, Quicksort_QB64 time was"; QuicksortQB64T#
  30.  
  31.  
  32. T# = Timer(.001)
  33. Quicksort_QB64_v2
  34. QuicksortQB64T# = Timer(.001) - T#
  35. For i = 1 To ArrayHigh - 1
  36.     If z(i) > z(i + 1) Then Print "NOT OK!": End
  37. Print "Quicksort_QB64_v2 checked, Quicksort_QB64_v2 time was"; QuicksortQB64T#
  38.  
  39.  
  40.  
  41. ' recursive
  42. Sub QuickSort (start As _Unsigned _Integer64, finish As _Unsigned _Integer64)
  43.     Lo = start: Hi = finish
  44.     Middle = n((Lo + Hi) \ 2)
  45.     Do
  46.         Do While n(Lo) < Middle: Lo = Lo + 1: Loop
  47.         Do While n(Hi) > Middle: Hi = Hi - 1: Loop
  48.         If Lo <= Hi Then
  49.             Swap n(Lo), n(Hi)
  50.             Lo = Lo + 1: Hi = Hi - 1
  51.         End If
  52.     Loop Until Lo > Hi
  53.     If Hi > start Then QuickSort start, Hi
  54.     If Lo < finish Then QuickSort Lo, finish
  55.  
  56. ' non recursive
  57. ' ref:  https://rosettacode.org/wiki/Sorting_algorithms/Quicksort#QB64
  58. Sub Quicksort_QB64
  59.     Left = LBound(m)
  60.     Right = UBound(m)
  61.     LeftMargin = Left
  62.     ReDim Stack&&(Left To Right)
  63.     StackPtr = 0
  64.     StackPtr = StackPtr + 1
  65.     Stack&&(StackPtr + LeftMargin) = Left
  66.     StackPtr = StackPtr + 1
  67.     Stack&&(StackPtr + LeftMargin) = Right
  68.     Do 'Until StackPtr = 0
  69.         Right = Stack&&(StackPtr + LeftMargin)
  70.         StackPtr = StackPtr - 1
  71.         Left = Stack&&(StackPtr + LeftMargin)
  72.         StackPtr = StackPtr - 1
  73.         Do 'Until Left >= Right
  74.             Pivot~&& = m((Left + Right) \ 2)
  75.             Indx = Left
  76.             Jndx = Right
  77.             Do
  78.                 Do While (m(Indx) < Pivot~&&)
  79.                     Indx = Indx + 1
  80.                 Loop
  81.                 Do While (m(Jndx) > Pivot~&&)
  82.                     Jndx = Jndx - 1
  83.                 Loop
  84.                 If Indx <= Jndx Then
  85.                     If Indx < Jndx Then Swap m(Indx), m(Jndx)
  86.                     Indx = Indx + 1
  87.                     Jndx = Jndx - 1
  88.                 End If
  89.             Loop While Indx <= Jndx
  90.             If Indx < Right Then
  91.                 StackPtr = StackPtr + 1
  92.                 Stack&&(StackPtr + LeftMargin) = Indx
  93.                 StackPtr = StackPtr + 1
  94.                 Stack&&(StackPtr + LeftMargin) = Right
  95.             End If
  96.             Right = Jndx
  97.         Loop Until Left >= Right
  98.     Loop Until StackPtr = 0
  99.  
  100.  
  101. Sub Quicksort_QB64_v2
  102.     Left = LBound(z)
  103.     Right = UBound(z)
  104.     ReDim Stack&&(Left To Right)
  105.     StackPtr = 0
  106.     StackPtr = StackPtr + 1
  107.     Stack&&(StackPtr + LeftMargin) = Left
  108.     StackPtr = StackPtr + 1
  109.     Stack&&(StackPtr + LeftMargin) = Right
  110.     Do 'Until StackPtr = 0
  111.         Right = Stack&&(StackPtr)
  112.         Left = Stack&&(StackPtr - 1)
  113.         StackPtr = StackPtr - 2
  114.         Do 'Until Left >= Right
  115.  
  116.             ' 'Classica' partitioning [
  117.             'Pivot~&& = z((Left + Right) \ 2)
  118.             'Indx = Left
  119.             'Jndx = Right
  120.             'Do
  121.             '    Do While (z(Indx) < Pivot~&&)
  122.             '        Indx = Indx + 1
  123.             '    Loop
  124.             '    Do While (z(Jndx) > Pivot~&&)
  125.             '        Jndx = Jndx - 1
  126.             '    Loop
  127.             '    If Indx <= Jndx Then
  128.             '        If Indx < Jndx Then Swap z(Indx), z(Jndx)
  129.             '        Indx = Indx + 1
  130.             '        Jndx = Jndx - 1
  131.             '    End If
  132.             'Loop While Indx <= Jndx
  133.             ' 'Classica' partitioning ]
  134.  
  135.             ' 'Magnetica' partitioning [
  136.             Jndx = Right
  137.             PL = Left
  138.             PR = Left
  139.             Swap z((Left + Right) \ 2), z(PL)
  140.             Do While PR < Jndx
  141.                 If z(PR) > z(PR + 1) Then
  142.                     Swap z(PL), z(PR + 1)
  143.                     PL = PL + 1
  144.                     PR = PR + 1
  145.                 ElseIf z(PR) = z(PR + 1) Then
  146.                     PR = PR + 1
  147.                 Else '<
  148.                     Swap z(PR + 1), z(Jndx)
  149.                     Jndx = Jndx - 1
  150.                 End If
  151.             Loop
  152.             Jndx = PL - 1
  153.             Indx = PR + 1
  154.             ' 'Magnetica' partitioning ]
  155.  
  156.             If Indx < Right Then
  157.                 StackPtr = StackPtr + 2
  158.                 Stack&&(StackPtr - 1) = Indx
  159.                 Stack&&(StackPtr) = Right
  160.             End If
  161.             Right = Jndx
  162.         Loop Until Left >= Right
  163.     Loop Until StackPtr = 0
  164.  

Consider having two records/fields (list of all living 7 billion people names with their birth date), now, the second column/field if it is to be sorted we cannot afford using the "Classica" partitioning because we enter the "few distinct keys" scenario with some 100 unique values. I am against "patches" of some pseudo-smart heuristics trying  to adapt "on the fly" to the type of keys in use, rather, I would prefer having two dedicated subroutines, one dealing with "few" the other with "many".
Of course if we can come up with fixing the "Magnetica" then there will be one subroutine only, nice and dirty-cheap, heh-heh.

Also, very often one needs sorting all the files on some drive by their year, not a good feeling 'Classica' to force you wait  7 seconds as in example above for 10,000,000 files.
In practice, there are more brutal sort cases, such as sorting occurrences of 600 billion English 5-grams (phrases of 5 words), the nasty 600GB x 4bytes =~2TB.

You are right @jack, but there is one aspect you should consider, being dirty from what standpoint!
As far as I know, using runtime stack (in addition to program's needs) is not a good practice, saw few discussions in the past where some Linux kernel gurus explicitly stated recursive code is not welcome at all, the topic was some very refined B-tree implementation, but recursive. Simply, the stack is considered an internal zone, endangered in case of entering deep recursion or for some reason stack being used extensively to the brim, hah-hah, hence the super popular stackoverflow site.

Oof, forgot to share the benchmarks done on some modern machines:
https://www.overclock.net/threads/benchmark-quicksort-says-sorting-2-billion-qwords.1794855/
« Last Edit: November 04, 2021, 04:07:51 pm by Sanmayce »
He learns not to learn and reverts to what all men pass by.