2

I have a model Quote, which has two properties: quote and probability. I want to select a random quote, but quotes with higher probability must be selected more often. For example, if we have

$q1->probability == 0.15

and

$q2->probability == 0.75

the latter must be 5 times more likely to be selected. The following command makes a random quote selection:

$quote = Quote::orderByRaw('RAND()')->first();

But I need the selection to be biased, as it was mentioned above. How to achieve this?

Alex Lomia
  • 6,705
  • 12
  • 53
  • 87

1 Answers1

3

I'm not sure if there is a way to just do this with MySQL, but this question has been raised and addressed with PHP before: Generating random results by weight in PHP?

Essentially, you want to pull Quote IDs and Weights ([ id => 1, weight => 0.5]) and the sum of all weights (0.90 in your example). Then, in no particular order, loop through the array and subtract each weight.

So if I have a MySQL table with these values,

[
    [ 'id' => 1, weight => 1 ],
    [ 'id' => 2, weight => 2 ],
    [ 'id' => 3, weight => 4 ],
]

You would then generate a number between 0 and 7, because that is the sum of all weights. At this point, you take each item and subtract it from the random number. An example would look like this.

$quoteWeights = Quote::select('id', 'weight')->get();
$weightSum    = $quoteWeights->sum('weight');
$weightRand   = mt_rand(0, $weightSum);

foreach ($quoteWeights as $quoteWeight)
{
    // Subtract our weight from the rand.
    $weightRand -= $quoteWeight->weight;

    // If it's bust, we want to return the winning model.
    if ($weightRand <= 0)
    {
        // Refresh the model so we get all attributes.
        return $quoteWeight->fresh();
    }
}

That's untested code, but I wager on it running correctly.

If you have a high-load server or a huge quotation database, you may want to invoke caching on th first part.

Community
  • 1
  • 1
Josh
  • 3,258
  • 2
  • 19
  • 31