Generating Random Results by Weight in PHP

Generate random numbers with weighted probabilites

The best you can do is generate a number between 0 and 100, and see in what range the number is:

$num=rand(0,100);

if ($num<10+40+35+5+5)
$result=2;

if ($num<10+40+35+5)
$result=3;

if ($num<10+40+35)
$result=4;

if ($num<10+40)
$result=5;

if ($num<10)
$result=6;

Be careful, your total probability isn't equal to 1, so sometimes $result is undefined

See @grigore-turbodisel 's answer if you want something that you can configure easily.

Pick random value by weight php

Instead of printing out the values as you are doing, you can just build a large array, and then choose a value randomly from that array.

while($r = $query->fetch()) {
for ( $i=0; $i <= $r["amount"]; $i++ ) {
// Add the user into the array as many times as they have tickets
$tickets[] = $r['userid'];
}
}

// select the first place winner
$first = $tickets[mt_rand(0, count($tickets) - 1)];

// remove the first place winner from the array
$tickets = array_values(array_filter($tickets, function($x) use ($first) {
return $x != $first;
}));

// select the second place winner
$second = $tickets[mt_rand(0, count($tickets) - 1)];

I'm sure there is a more efficient way to do this using math, but I need to think about it a bit more...

Random value from array by weight in php

I think something like this will do what you want:

sample

(click the submit button multiple times on the sample to get the code to re-execute)

$fruits = array('apple' => '20', 'orange' => '40', 'pear' => '40');

$newFruits = array();
foreach ($fruits as $fruit=>$value)
{
$newFruits = array_merge($newFruits, array_fill(0, $value, $fruit));
}

$myFruit = $newFruits[array_rand($newFruits)];

This creates an array ($newFruits), which is a numerically-indexed array with 100 elements. 20 of those elements are 'apple', 40 are 'orange', and 40 are 'pear'. Then we select a random index from that array. 20 times out of 100 you will get 'apple', 40 times out of 100 you will get 'orange', and 40 times out of 100 you will get 'pear'.

Weighted Random Choice In PHP

mt_rand returns an integer so comparing it to 0.02 is effectively the same as comparing it to 1. Hence you always get around 1% for the weights which are less than 1%. Try computing $num like this instead:

$num = mt_rand(0, array_sum($weights) * 100) / 100; 

Demo on 3v4l.org

How do you generate random results by weight with Twig?

Solved it with this:

{% set linkWeight = 0 %}
{% for item in 'wiki.resources.items'|translations %}
{% set linkWeight = linkWeight + item.weight %}
{% endfor %}
{% set randomWeight = random(1,linkWeight) %}
{% set linkArr = [] %}
{% for item in 'wiki.resources.items'|translations %}
{% set randomWeight = randomWeight - item.weight %}
{% if randomWeight <= 0 %}
{% set linkArr = linkArr|merge([item]) %}
{% endif %}
{% endfor %}
{% for item in linkArr[:3] %}
<div class="ressources__item col-12 col-md-4 col-lg-12">
<a href="{{ item.href }}">
<h3 class="ressources__sub">{{ item.h3 }} </h3>
</a>
<p>{{ item.p }}</p>
</div>
{% endfor %}

Basically, I changed the "tier" keys in the JSON file to "weight" and added a larger number for what was previously Tier1 and the opposite for Tier3.

If you're curious about how it works, here's the article that helped me make logical sense of what I needed to achieve. The only difference here is that I made another array with the weighted random selection so that I could only display 3 from the selection at a time (as can be seen here: {% for item in linkArr[:3] %})

Get a weighted random sample from a dataset

the easiest way is to dynamically adjust the weight so for instance take the initial weight and multiply it by the number iterations of the calls since that country has been called. then simply sort the list by wieghted order. So for instance the USA will be moved below smaller GDP countries based on how long those countries have been waiting in the queue

Generate random numbers with fix probability

The single probability check with linear probability can be easily done with:

function checkWithProbability($probability=0.1, $length=10000)
{
$test = mt_rand(1, $length);
return $test<=$probability*$length;
}

For example, this will produce:

for($i=0; $i<10; $i++)
{
var_dump(checkWithProbability(1/3));
}

Something like:


bool(false)
bool(true)
bool(false)
bool(false)
bool(false)
bool(false)
bool(false)
bool(false)
bool(true)
bool(false)

And you can use that principle to get your edges check with desired probability:

function checkWithSet(array $set, $length=10000)
{
$left = 0;
foreach($set as $num=>$right)
{
$set[$num] = $left + $right*$length;
$left = $set[$num];
}
$test = mt_rand(1, $length);
$left = 1;
foreach($set as $num=>$right)
{
if($test>=$left && $test<=$right)
{
return $num;
}
$left = $right;
}
return null;//debug, no event realized
}

The idea is to use geometry probability - i.e. split some line part into pieces with corresponding length and then check to which part our random number belongs.



0.75 0.9
| |
V V
*--------*--*-----*-*--*--* <-- (length)
^ ^ ^ ^ ^
| | | | |
0 0.4 0.5 0.8 1

Sample will be:

$set = [
1 => 0.4,
2 => 0.1,
3 => 0.25,
4 => 0.05,
5 => 0.1,
6 => 0.1
];
for($i=0; $i<10; $i++)
{
var_dump(checkWithSet($set));
}

With result like:


int(1)
int(2)
int(2)
int(6)
int(3)
int(1)
int(1)
int(6)
int(1)
int(1)

You can increase $length - in theory, this will increase "quality" of randomize check, but that's not too easy thing - because mt_rand() uses pseudo-random generator, Mersenne Twister (and in ideal case that's not true linear probability)

Random weighted selection of an event

Two ways to do this, that I can think of from the top of my head:

Option 1: Fill a new array with the key values from the set of data, where the weight determines how often an item is repeated. The proportion in this array then matches the weighted distribution. Simply grab using $arr[array_rand($arr)]. While simple and easy to understand, this will explode in your face if there are a LOT of items, or if the weight values are really high.

$weighted = array();
foreach($items as $item) {
array_merge($weighted, array_fill(0, $item['weight'], $item['value']);
}
$result = $weighted[array_rand($weighted)];

Option 2. Sum the weights. Pick a random number between 0 and sum-of-weights. Loop over the elements in the dataset, compare against the random number you picked. As soon as you hit one that is equal to or bigger then the random index, select that element.

function findRandomWeighted(array $input) {
$weight = 0;
// I'm assuming you can get the weight from MySQL as well, so this loop really should not be required. In that case $weight becomes a parameter.
foreach($items as $item) {
$weight += $item['weight'];
}

$index = rand(1, $weight);
foreach($items as $item) {
$index -= $item['weight'];
if($index <= 0) { return $item['value'] }
}

return null;
}

Following our conversation in the comments below, here is a Pastebin with the code in it:

http://pastebin.com/bLbhThhj



Related Topics



Leave a reply



Submit