Notifications
Clear all

[Closed] Qsort Challenge

I’d never had a need to use qSort before today and I found it a bit confusing at first, but now I’ve found my solution I thought I’d put a little challenge up to see who can make the best solution… is there a better way than qSort?

vals = #(#(0,400,1),#(0,100,2),#(10,200,3),#(0,200,4),#(0,300,5))

--I Need it to return in this order, so sorted by 2nd value of array then first, but ignoring the 3rd.
--#(0,100,2)
--#(0,200,4)
--#(10,200,3)
--#(0,300,5)
--#(0,400,1)
18 Replies
1 Reply
(@denist)
Joined: 11 months ago

Posts: 0

the question is how many items in the array… the best solution might be different for 1000 and 100,000

In this particular instance not many, probably under 100… but it would be interesting to see the comparisons? Perhaps someone else wants to join the party before Denis owns this one

if we talk about qsort i think that will be hard to beat just simple:


fn sortByIndex v1 v2 = if (v1[1] < v2[1]) then -1 else if (v1[1] > v2[1]) then 1 else if (v1[2] < v2[2]) then -1 else if (v1[2] > v2[2]) then 1 else 0

here is a template to play:


seed 0 
num = 10000
arr = for k=1 to num collect #(random 0 1000,random 0 1000,random 0 1000)

fn sortByIndex v1 v2 = if (v1[1] < v2[1]) then -1 else if (v1[1] > v2[1]) then 1 else if (v1[2] < v2[2]) then -1 else if (v1[2] > v2[2]) then 1 else 0

t1 = timestamp()
m1 = heapfree
qsort arr sortByIndex
format "time:% memory:%
" (timestamp() - t1) (m1 - heapfree)
arr

for the sample array above …
using c# on-fly assembly VS mxs qsort:

C# time:44 memory:2400280L
MXS time:240 memory:2227016L

so c# solution is much faster

4 Replies
(@polytools3d)
Joined: 11 months ago

Posts: 0

How do you pass a ND Array to #C?
The only way I’ve found is to convert it to 1D Array.

(@denist)
Joined: 11 months ago

Posts: 0

this is the base c# sort function (sort by first and by next) :


 public static object[][] Sort(object[][] array)
 {
 	Array.Sort(array, delegate(object[] x, object[] y)
 	{
 		int result = (x[0] as IComparable).CompareTo(y[0]);
 		return (result != 0) ? result : (x[1] as IComparable).CompareTo(y[1]);
 	});
 	return array;	
 }
 

array of several types (integer, float, string) max automatically converts to object[]

(@polytools3d)
Joined: 11 months ago

Posts: 0

Thank you Denis!
And for Point2/Point3 they should be converted to array first?

#( #([1,1,1],[2,2,2],[3,3,3]), #([4,4,4],[5,5,5],[6,6,6]) )
#( #(#(1,1,1),#(2,2,2),#(3,3,3)), #(#(4,4,4),#(5,5,5),#(6,6,6)) )

(@denist)
Joined: 11 months ago

Posts: 0

true… only three base mxs types max aromatically converts to .net array (object)… also arrays of .net objects (classes)

Aha but your code is incorrect Denis…

Yours returns…
#(#(0, 100, 2), #(0, 200, 4), #(0, 300, 5), #(0, 400, 1), #(10, 200, 3))

not
#(#(0, 100, 2), #(0, 200, 4), #(10, 200, 3), #(0, 300, 5), #(0, 400, 1))

1 Reply
(@denist)
Joined: 11 months ago

Posts: 0

it’s because you Need it to return in this order, so sorted by 2nd value of array then first
i’ve sorted in the order “first-second”

here is what you asked. but the performance must be the same:


fn sortByIndex v1 v2 = if (v1[2] < v2[2]) then -1 else if (v1[2] > v2[2]) then 1 else if (v1[1] < v2[1]) then -1 else if (v1[1] > v2[1]) then 1 else 0

time:283 memory:1384L

My Solution…

seed 0 
num = 10000
arr = for k=1 to num collect #(random 0 1000,random 0 1000,random 0 1000)

fn compareFN v1 v2 =
(
	Case of
	(
		((v1[2]) == (v2[2]) and (v1[1] < v2[1])) : -1
		((v1[2]) == (v2[2]) and (v1[1] >= v2[1])) : 1
		((v1[2]) < (v2[2])) : -1
		((v1[2]) > (v2[2])) : 1
		default: 0
	)
)

t1 = timestamp()
m1 = heapfree
qsort arr compareFN
format "time:% memory:%
" (timestamp() - t1) (m1 - heapfree)
arr
3 Replies
(@denist)
Joined: 11 months ago

Posts: 0

marked by red is incorrect. i’ve explained already on this forum why.

(@davewortley)
Joined: 11 months ago

Posts: 0

I’ve searched but can’t find this point you’re raising…

(@denist)
Joined: 11 months ago

Posts: 0
global i
 fn sortCorrect v1 v2 = 
 (
 	i += 1
 	if v1 < v2 then -1 else if v1 > v2 then 1 else 0
 )
 fn sortIncorrect v1 v2 = 
 (
 	i += 1
 	if v1 < v2 then -1 else if v1 >= v2 do 1
 )
 	
 (
 	seed (i = 0)
 	arr = for k=1 to 100 collect (random 1 3)
 
 	t1 = timestamp()
 	m1 = heapfree
 	qsort arr sortCorrect
 	format "correct iterations:% time:% memory:%
" i (timestamp() - t1) (m1 - heapfree)
 	arr
 )
 (
 	seed (i = 0)
 	arr = for k=1 to 100 collect (random 1 3)
 
 	t1 = timestamp()
 	m1 = heapfree
 	qsort arr sortIncorrect
 	format "incorrect iterations:% time:% memory:%
" i (timestamp() - t1) (m1 - heapfree)
 	arr
 )

check this snippet…
qsort – it’s “quick sort” algorithm. it needs to know every time if “pivot” element less, equals, or greater than a “compared to”. it works with only two conditions (for example – “less and not less”), but it stays in-place when three-condition algorithm goes to the next element in case of “equals”.
try to run the same snippet for 1,000 elements… 10,000… don’t try 100,000!

This is the only thing I can think of…

        Case of
  	(
  		((v1[2]) == (v2[2]) and (v1[1] < v2[1])) : -1
                ((v1[2]) == (v2[2]) and (v1[1] == v2[1])) : 0
  		((v1[2]) == (v2[2]) and (v1[1] > v2[1])) : 1
  		((v1[2]) < (v2[2])) : -1
  		((v1[2]) > (v2[2])) : 1
  		default: 0
  	)

100
1000
10000

Yeah I’m not going to try 100000

OK
sortCorrect()
sortIncorrect()
correct iterations:399 time:1 memory:3392L
#(1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, …)
incorrect iterations:1165 time:3 memory:38096L
#(1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, …)
OK
sortCorrect()
sortIncorrect()
correct iterations:3332 time:10 memory:159016L
#(1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, …)
incorrect iterations:86508 time:157 memory:4818376L
#(1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, …)
OK
sortCorrect()
sortIncorrect()
correct iterations:36699 time:74 memory:2023344L
#(1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, …)
incorrect iterations:8388633 time:16438 memory:23001380L
#(1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, …)

So by having my function as…

        Case of
  	(
  		((v1[2]) == (v2[2]) and (v1[1] < v2[1])) : -1
               	((v1[2]) == (v2[2]) and (v1[1] > v2[1])) : 1
  		((v1[2]) < (v2[2])) : -1
  		((v1[2]) > (v2[2])) : 1
  		default: 0
  	)

I Should be ok?

1 Reply
(@denist)
Joined: 11 months ago

Posts: 0

that’s OK. but if you would count the worse case you see that it needs make 6 comparisons.
why? if you can do the same for only 4?


fn sortByIndex v1 v2 = if (v1[2] < v2[2]) then -1 else if (v1[2] > v2[2]) then 1 else if (v1[1] < v2[1]) then -1 else if (v1[1] > v2[1]) then 1 else 0