Tuesday, October 21, 2008

Unique Random Numbers & Random Rows from Large ActiveRecord Tables

I was looking for an easy way to get unique and random numbers. In my case, I need a small quantity of numbers from a large pool. Here is the best solution I could come up with (derived from a message board):

def unique_randoms(n, max = nil)
seen = {}
n.times{x = rand max; seen[x] ? redo : seen[x] = 1}
seen.keys
end

It's a little more terse than it needs to be. Here is a more explicit version:

def unique_randoms(n, max = nil)
seen = {}
n.times do
x = rand max
redo if seen[x]
seen[x] = true
end
seen.keys
end

If you need a large number of randoms it is faster to use the Array sort method:

def unique_randoms(n, max)
(0..max).sort{rand}[0..n]
end

This leads us to a solution to quickly getting random rows through ActiveRecord:

def random(scope, num_randoms)
count = scope.count
unique_randoms(num_randoms, count){|i| scope.first :offset => i}
end

The above solution is appropriate for large result sets where :order => 'RAND()' is inefficient, and for small quantities of random rows. Each random row adds a query to this solution.