2011-03-23 70 views
3

我在Rails中有一个模型,我想从中选择一个随机条目。 到目前为止,我已经有了这样的命名范围内做到了:在ruby/rails中从数组中加权的随机选择

named_scope :random, lambda { { :order=>'RAND()', :limit => 1 } } 

但现在我已经添加了一个整型字段“重量”,以表示与各行应挑选的概率模型。

我该如何做一个加权随机选择?

我发现并尝试了snippets.dzone.com上的两种方法,它扩展了Array类并添加了加权随机函数,但两者都无效或为我选取了随机项。

我正在使用REE 1.8.7和Rails 2.3。

+1

启发这可能是很好的DB你使用的是什么补充。在Ruby中做并不难,但在数据库中做它可能会更有效率。 – 2011-03-23 14:28:50

+0

哦,是的,忘了提及我正在使用MySQL。但我非常专注于解决Ruby中的问题,甚至没有考虑在查询级别解决这个问题。 – capsized 2011-03-24 08:02:34

回答

5

也许我明白这是完全错误的,但是难道你不能仅仅使用列“权重”作为随机数的一个因子吗? (根据分贝,一些预防措施是必要的,以防止产品溢出。)

named_scope :random, lambda { { :order=>'RAND()*weight', :limit => 1 } } 
+0

你是正确的重量是随机数的因素。这比我最初想象的要简单得多!谢谢! – capsized 2011-03-24 08:00:48

+2

这不会按预期工作。假设一张有999条记录的表格,所有的权重都是1,除了一个是2的情况。你会希望特殊记录选择1/500次(因为它的权重是2,总重量是1000),但实际上它是选择一半的时间(每次rand()计算大于1)。而且,即使你只记录一条记录,它也会执行rand()函数的1000倍。您最好在一个查询中获得总数,使用ruby在权重间隔中选择一个点,然后根据该数字选择一条记录。 – rewritten 2012-11-27 15:57:31

0

在一个查询,你应该:

  • 由随机因素计算总重量
  • 乘法,给出一个权重阈值
  • 再次通过表格求和,直到达到重量阈值。

在SQL它会sompething这样(没试过真正)

SELECT SUM(weight) FROM table INTO @totalwt; 
@lim := FLOOR(RAND() * @totalwt); 
SELECT id, weight, @total := @total + weight AS cumulativeWeight 
    FROM table WHERE cumulativeWeight < @lim, (SELECT @total:=0) AS t; 

通过Optimal query to fetch a cumulative sum in MySQL