# Timing different way to shuffle an array in ruby
# the "assembler" way of swapping two variable
values
# 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
# shuffling with the ruby way of swapping
# shuffling with the "assembler" way of swapping
# To inspect shuffling methods, invoke with argument "test"
and optionally, size
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
def shuffle_V
(length - 1).downto 1 do |slot|
source = rand(slot + 1)
self[slot], self[source] = self[source], self[slot]
end
self
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
puts '--'
puts (0..size).to_a.shuffle_V
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}"
shuffle_time_V = time_shuffle_method(500) { |a| a.shuffle_V }
puts "Time elapsed for shuffle_V : #{shuffle_time_V
}"
=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@[~/ruby/steam]: ruby /tmp/shuffle_timer
Time elapsed for shuffle! : 4.352824
Time elapsed for shuffle_II : 6.03016
Time elapsed for shuffle_III : 3.088946
Time elapsed for shuffle_IV : 3.890105
Time elapsed for shuffle_V : 6.351477
stephan@[~/ruby/steam]: ruby /tmp/shuffle_timer
Time elapsed for shuffle! : 4.382251
Time elapsed for shuffle_II : 6.046666
Time elapsed for shuffle_III : 3.220461
Time elapsed for shuffle_IV : 4.00079
Time elapsed for shuffle_V : 6.292253
=end