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
Create Subdomains on the Fly With .Htaccess (PHP)
Remove a Child With a Specific Attribute, in Simplexml For PHP
Laravel 5 - Remove Public from Url
What Is the Size Limit of a Post Request
Htmlentities() Vs. Htmlspecialchars()
How to Convert Xml into Array in PHP
How to Convert Json String to Array
PHP Display Image Blob from MySQL
PHP - Should I Call Exit() After Calling Location: Header
Creating Default Object from Empty Value in PHP
PHP Fatal Error: Using $This When Not in Object Context
Passing Base64 Encoded Strings in Url
MySQL VS MySQLi When Using PHP
Filter/Remove Rows Where Column Value Is Found More Than Once in a Multidimensional Array
Commands Out of Sync; You Can't Run This Command Now
MySQLi Update Throwing Call to a Member Function Bind_Param() Error