Jeremy McAnally wrote:
Just to save you a little time:
myarr = [1,2,3,4,5]
=> [1, 2, 3, 4, 5]
myarr.sort_by { rand }
=> [5, 3, 2, 4, 1]
–Jeremy
This doesn’t seem to belong in a rails discussion, but I was curious and
looked at performance. It seems the elegant ways are not the best. See
ruby code below.
Stephan
Timing different way to shuffle an array in ruby
class Array
The ruby way of swapping two variable values
def swap!(a,b)
self[a], self[b] = self[b], self[a]
end
the “assembler” way of swapping two variable values
def swap_with_int_var!(a,b)
c = self[a]
self[a] = self[b]
self[b] = c
end
Shuffling arrays is usually done by iterating through the indices,
and swapping
each value with a value at a random position
shuffling based on push/delete
def shuffle!
size.downto(1) { |n| push delete_at(rand(n)) }
self
end
shuffling with the ruby way of swapping
def shuffle_II
(size-1).downto(1) { |n| swap!(n, rand(size)) }
self
end
shuffling with the “assembler” way of swapping
def shuffle_III
(size-1).downto(1) { |n| swap_with_int_var!(n, rand(size)) }
self
end
def shuffle_IV
sort_by { rand }
end
end
def time_shuffle_method(size)
a = (0…size).to_a
start_time = Time.new
3000.times { yield a }
end_time = Time.new
return end_time - start_time
end
To inspect shuffling methods, invoke with argument “test” and
optionally, size
if (ARGV[0] == ‘test’)
size = ARGV[1] ? ARGV[1].to_i : 5
puts (0…size).to_a.shuffle!
puts ‘–’
puts (0…size).to_a.shuffle_II
puts ‘–’
puts (0…size).to_a.shuffle_III
puts ‘–’
puts (0…size).to_a.shuffle_IV
exit
end
shuffle_time = time_shuffle_method(500) { |a| a.shuffle! }
puts “Time elapsed for shuffle! : #{shuffle_time}”
shuffle_time_II = time_shuffle_method(500) { |a| a.shuffle_II }
puts “Time elapsed for shuffle_II : #{shuffle_time_II}”
shuffle_time_III = time_shuffle_method(500) { |a| a.shuffle_III }
puts “Time elapsed for shuffle_III : #{shuffle_time_III}”
shuffle_time_IV = time_shuffle_method(500) { |a| a.shuffle_IV }
puts “Time elapsed for shuffle_IV : #{shuffle_time_IV}”
=begin
Here are results from running on my machine
Conclusion: fancy ruby manipulations take their time
Stick with the “ordinary” shuffle implementation ( shuffle_III )
stephan@[/tmp]: ruby shuffle_timer
Time elapsed for shuffle! : 4.319548
Time elapsed for shuffle_II : 5.956232
Time elapsed for shuffle_III : 3.011474
Time elapsed for shuffle_IV : 3.672521
stephan@[/tmp]: ruby shuffle_timer
Time elapsed for shuffle! : 4.30994
Time elapsed for shuffle_II : 5.902858
Time elapsed for shuffle_III : 3.139915
Time elapsed for shuffle_IV : 3.692018
Stephan W.
January 2007
=end