Sort_by { rand } not working

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

2007/8/26, Mike D. [email protected]:

end

Hi,

@all.sort_by { rand } returns a randomly sorted array, but leaves @all
intact. Perhaps you wanted @all = @all.sort_by { rand }?

Regards,
R.

On Aug 26, 7:25 am, Mike D. [email protected]
wrote:

@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 }

On Sun, 26 Aug 2007 09:22:38 -0500, Ken B. wrote:

  @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

On Sun, 26 Aug 2007 22:25:08 +0900, Mike D. wrote:

#current not working
@all.sort_by { rand }

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

–Ken

On 8/26/07, Ken B. [email protected] wrote:

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

try

sort!{rand}

kind regards -botp

On Aug 26, 2007, at 12:31 PM, botp wrote:

On 8/26/07, Ken B. [email protected] wrote:

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

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 G. II

botp wrote:

On 8/26/07, Ken B. [email protected] wrote:

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

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

James Edward G. II wrote:

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

James G. wrote:

On Aug 26, 2007, at 12:31 PM, botp wrote:

On 8/26/07, Ken B. [email protected] wrote:

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

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 G. II

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

Regards
Stefan

Mike D. wrote:

#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 [email protected]
     j = i+rand(@all.length-i)
     @all[i], @all[j] = @all[j], @all[i]
 end

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

Dan

Lionel B. wrote:

You could even optimize this further with [email protected] instead of
[email protected] (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

Lionel B. wrote:

Dan Z. wrote:

for i in [email protected]
    j = i+rand(@all.length-i)
    @all[i], @all[j] = @all[j], @all[i]
end

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

You could even optimize this further with [email protected] instead of
[email protected] (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 :slight_smile: (IMHO more readable, speed wise
it is faster for small arrays, slower for big arrays, break even is here
around 100 items).

Regards
Stefan

Dan Z. wrote:

for i in [email protected]
    j = i+rand(@all.length-i)
    @all[i], @all[j] = @all[j], @all[i]
end

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

You could even optimize this further with [email protected] instead of
[email protected] (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

On 8/26/07, Stefan R. [email protected] wrote:

You could even use @all.length.times :slight_smile: (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 { … } ?

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

Dan Z. wrote:

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

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

[email protected] wrote:

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

Lloyd L. wrote:

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

This forum is not affiliated to the Ruby language, Ruby on Rails framework, nor any Ruby applications discussed here.

| Privacy Policy | Terms of Service | Remote Ruby Jobs