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

···

--
Posted via http://www.ruby-forum.com/.

Hi,

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

Regards,
R.

···

2007/8/26, Mike Dershowitz <michael.dershowitz@jpmchase.com>:

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
--
Posted via http://www.ruby-forum.com/\.

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

Try @all = @all.sort_by{ rand }

···

On Aug 26, 7:25 am, Mike Dershowitz <michael.dershow...@jpmchase.com> wrote:

    @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

···

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

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 }

--
Ken Bloom. PhD candidate. Linguistic Cognition Laboratory.
Department of Computer Science. Illinois Institute of Technology.
http://www.iit.edu/~kbloom1/

Mike Dershowitz wrote:

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 0...@all.length
         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

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

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

--Ken

···

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

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

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 Bloom. PhD candidate. Linguistic Cognition Laboratory.
Department of Computer Science. Illinois Institute of Technology.
http://www.iit.edu/~kbloom1/

Dan Zwell wrote:

    for i in 0...@all.length
        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 0...@all.length-1 instead of
0...@all.length (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

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

···

thomas.macklin@gmail.com wrote:

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

try

    sort!{rand}

kind regards -botp

···

On 8/26/07, Ken Bloom <kbloom@gmail.com> wrote:

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

Lionel Bouton wrote:

Dan Zwell wrote:

    for i in 0...@all.length
        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 0...@all.length-1 instead of
0...@all.length (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 Bouton wrote:

Dan Zwell wrote:

    for i in 0...@all.length
        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 0...@all.length-1 instead of
0...@all.length (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

···

--
Posted via http://www.ruby-forum.com/\.

Dan Zwell 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
    ar.delete_at(x)
  end
  arr
end

data = (0..9).to_a

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

···

--
Posted via http://www.ruby-forum.com/\.

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 Gray II

···

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

On 8/26/07, Ken Bloom <kbloom@gmail.com> wrote:

botp wrote:

···

On 8/26/07, Ken Bloom <kbloom@gmail.com> 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
--
Posted via http://www.ruby-forum.com/\.

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 { ... } ?

···

On 8/26/07, Stefan Rusterholz <apeiros@gmx.net> wrote:

Regards
Stefan
--
Posted via http://www.ruby-forum.com/\.

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

···

--
Posted via http://www.ruby-forum.com/.

James Gray wrote:

···

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

On 8/26/07, Ken Bloom <kbloom@gmail.com> 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 Gray II

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

Regards
Stefan
--
Posted via http://www.ruby-forum.com/\.

James Edward Gray 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
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

···

from my bench results it's a waste of time (it's even dangerous, see

Lloyd Linklater 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