sort_by { rand } not working

M

Mike Dershowitz

Hello:

I've got an array of arrays that I'd like to sort_by random. each
individual array is just a hash of a value and then an object. Is there
some reason why sort_by { rand } wouldn't work? My code is simple (teams
is populated with a db call):

@teams.each do |t|
@all << ["t",t]
end
#current not working
@all.sort_by { rand }

Any ideas? Am I stretching the limit of the rand function?

Thanks!

Mike
 
P

Phrogz

@teams.each do |t|
@all << ["t",t]
end
#current not working
@all.sort_by { rand }

#sort_by doesn't modify the original array, it returns a new one.

Try @all = @all.sort_by{ rand }
 
K

Ken Bloom

Hello:

I've got an array of arrays that I'd like to sort_by random. each
individual array is just a hash of a value and then an object. Is there
some reason why sort_by { rand } wouldn't work? My code is simple (teams
is populated with a db call):

@teams.each do |t|
@all << ["t",t]
end
#current not working
@all.sort_by { rand }

sort_by returns the sorted list
sort_by! sorts the list in place

--Ken
 
K

Ken Bloom

Hello:

I've got an array of arrays that I'd like to sort_by random. each
individual array is just a hash of a value and then an object. Is
there some reason why sort_by { rand } wouldn't work? My code is simple
(teams is populated with a db call):

@teams.each do |t|
@all << ["t",t]
end
#current not working
@all.sort_by { rand }

sort_by returns the sorted list
sort_by! sorts the list in place

Whoops. As soon as I posted, I noticed that there is no sort_by!.

--Ken
 
J

James Edward Gray II

try

sort!{rand}

That's not a random sort. In fact, it's equivalent to sort! { 1 }:
data =3D (0..9).to_a =3D> [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
data.sort! { rand } =3D> [9, 5, 0, 6, 2, 7, 4, 8, 3, 1]
data.sort! { rand } =3D> [1, 7, 9, 4, 0, 8, 2, 3, 6, 5]
data.sort! { rand } =3D> [5, 8, 1, 2, 9, 3, 0, 6, 4, 7]
data =3D (0..9).to_a =3D> [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
data.sort! { 1 } =3D> [9, 5, 0, 6, 2, 7, 4, 8, 3, 1]
data.sort! { 1 } =3D> [1, 7, 9, 4, 0, 8, 2, 3, 6, 5]
data.sort! { 1 }
=3D> [5, 8, 1, 2, 9, 3, 0, 6, 4, 7]

It would be better to use:

data =3D data.sort_by { =85 }

James Edward Gray II=
 
S

Stefan Rusterholz

botp said:
try

sort!{rand}


kind regards -botp

That's equivalent to sort! { 1 } and rather predictable (sort/sort!
expects -1, 0 or 1 as value, rand is 0..1) and hence IMHO pointless.

Regards
Stefan
 
S

Stefan Rusterholz

James said:
try

sort!{rand}

That's not a random sort. In fact, it's equivalent to sort! { 1 }:
data = (0..9).to_a => [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
data.sort! { rand } => [9, 5, 0, 6, 2, 7, 4, 8, 3, 1]
data.sort! { rand } => [1, 7, 9, 4, 0, 8, 2, 3, 6, 5]
data.sort! { rand } => [5, 8, 1, 2, 9, 3, 0, 6, 4, 7]
data = (0..9).to_a => [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
data.sort! { 1 } => [9, 5, 0, 6, 2, 7, 4, 8, 3, 1]
data.sort! { 1 } => [1, 7, 9, 4, 0, 8, 2, 3, 6, 5]
data.sort! { 1 }
=> [5, 8, 1, 2, 9, 3, 0, 6, 4, 7]

It would be better to use:

data = data.sort_by { � }

James Edward Gray II

Well, it has the (extremely little) chance of being 0, which introduces
a very very small randomness :)

Regards
Stefan
 
L

Lionel Bouton

James said:
It would be better to use:

data = data.sort_by { … }

I was wondering about a crazy idea of mine: "sort!{rand<=>rand}" and
from my bench results it's a waste of time (it's even dangerous, see
below). I suspect sort_by begins by mapping the block results and then
sorts based on the map (it's roughly 4x faster than sort!{ rand <=> rand }).
sort!{rand<=>rand} obviously can't do that and must call the block each
time a comparison must be done (I suspect that more rand calls make it
slower even if it can avoid copying things and instead do in-place
modifications ... or that the Ruby sort algorithm doesn't like
comparison results changing...).

Obviously depending on the sort algorithm used it might not even
converge on a result (the dangerous part...).

Lionel
 
D

Dan Zwell

Mike said:
Hello:

I've got an array of arrays that I'd like to sort_by random. each
individual array is just a hash of a value and then an object. Is there
some reason why sort_by { rand } wouldn't work? My code is simple (teams
is populated with a db call):

@teams.each do |t|
@all << ["t",t]
end
#current not working
@all.sort_by { rand }

Any ideas? Am I stretching the limit of the rand function?

Thanks!

Mike

Mike,

This has been discussed here before. Your two best choices are:
@all = @all.sort_by {rand}
or

for i in (e-mail address removed)
j = i+rand(@all.length-i)
@all, @all[j] = @all[j], @all
end

The second one will be faster, but you probably shouldn't care on small
arrays.

Dan
 
L

Lionel Bouton

Dan said:
for i in (e-mail address removed)
j = i+rand(@all.length-i)
@all, @all[j] = @all[j], @all
end

The second one will be faster, but you probably shouldn't care on
small arrays.


You could even optimize this further with (e-mail address removed)-1 instead of
(e-mail address removed) (the last loop is a noop), but I agree, this is too much
pain for the eyes to bother unless it becomes a real performance problem.

Sorry if this has already been said, in fact I didn't even remember this
code on the mailing-list at all (too much traffic for me maybe).

Lionel
 
D

Dan Zwell

Lionel said:
Dan said:
for i in (e-mail address removed)
j = i+rand(@all.length-i)
@all, @all[j] = @all[j], @all
end

The second one will be faster, but you probably shouldn't care on
small arrays.


You could even optimize this further with (e-mail address removed)-1 instead of
(e-mail address removed) (the last loop is a noop), but I agree, this is too much
pain for the eyes to bother unless it becomes a real performance problem.

Sorry if this has already been said, in fact I didn't even remember this
code on the mailing-list at all (too much traffic for me maybe).

Lionel


Heh, you're right about the noop, and that hasn't been said. This code
was never on the list in this form--those 4 lines are a condensation of
something I posted to this list from my algorithms book, and someone's
suggestion that optimized it further (use parallel assignment instead of
making a swap() function).

Dan
 
S

Stefan Rusterholz

Lionel said:
Dan said:
for i in (e-mail address removed)
j = i+rand(@all.length-i)
@all, @all[j] = @all[j], @all
end

The second one will be faster, but you probably shouldn't care on
small arrays.


You could even optimize this further with (e-mail address removed)-1 instead of
(e-mail address removed) (the last loop is a noop), but I agree, this is too much
pain for the eyes to bother unless it becomes a real performance
problem.


You could even use @all.length.times :) (IMHO more readable, speed wise
it is faster for small arrays, slower for big arrays, break even is here
around 100 items).

Regards
Stefan
 
L

Logan Capaldo

You could even use @all.length.times :) (IMHO more readable, speed wise
it is faster for small arrays, slower for big arrays, break even is here
around 100 items).
@all.each_index { ... } ?
Regards
Stefan
 
T

thomas.macklin

If you don't mind removing duplicates, Sets are non-ordered, so you
could do
s = Set.new
@all.each do |e| s.add(e) end

That will be non-ordered, but duplicates will get stripped.

Tom
 
D

Dan Zwell

If you don't mind removing duplicates, Sets are non-ordered, so you
could do
s = Set.new
@all.each do |e| s.add(e) end

That will be non-ordered, but duplicates will get stripped.

Tom

Non-ordered isn't the same as randomized. Sets most certainly are
ordered, we (the non-ruby-core-developers) just don't know how. The
point is that they aren't guaranteed to be random. Try running the above
code and see--it results in the same ordering each time, regardless of
the order in which the elements are added. For some data sets, it will
probably appear more random than others.

Dan
 
L

Lloyd Linklater

Dan said:
Non-ordered isn't the same as randomized.

I apologize mos sincerely for posting actual code but what about this
for randomizing:

def arr_rand(ar)
arr = []
while ar.length > 0 do
x = rand(ar.length)
arr << ar[x]
ar.delete_at(x)
end
arr
end

data = (0..9).to_a

10.times do
data = arr_rand(data)
p data
end
 
L

Lloyd Linklater

dang it! I *knew* that I should have been thinking more ruby and less
pascal. This may look more rubyish:

def arr_rand(ar)
arr = []
while ar.length > 0 {arr << ar.delete_at(rand(ar.length))}
arr
end
 
D

Dan Zwell

Lloyd said:
dang it! I *knew* that I should have been thinking more ruby and less
pascal. This may look more rubyish:

def arr_rand(ar)
arr = []
while ar.length > 0 {arr << ar.delete_at(rand(ar.length))}
arr
end

Yes, that is fairly simple to understand. But it is equivalent to
sort_by {rand} in that it does not actually change the array that is
passed in. How about this variation?

def randomize!(ary)
temp = ary.dup
ary.clear
ary << temp.delete_at(rand(temp.length)) until temp.empty?
ary # this line is optional--want to return the array
# we've just randomized?
end


Dan
 
L

Lloyd Linklater

Dan said:
Lloyd said:
dang it! I *knew* that I should have been thinking more ruby and less
pascal. This may look more rubyish:

def arr_rand(ar)
arr = []
while ar.length > 0 {arr << ar.delete_at(rand(ar.length))}
arr
end

Yes, that is fairly simple to understand. But it is equivalent to
sort_by {rand} in that it does not actually change the array that is
passed in.

Actually, it was different. The sort_by {rand} gives the same sequence
of shuffling every time you run it. Mine is different every time. I
get a bang out of the declaration you have. That is a nice touch. I
also like your use of until temp.empty? That is very Ruby-ish to me.
:)
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

No members online now.

Forum statistics

Threads
473,744
Messages
2,569,484
Members
44,903
Latest member
orderPeak8CBDGummies

Latest Threads

Top