How would I go about creating a random number generator which has a bias to be within a range of numbers?
say I have this:
$rnum = rand(0,200);
so $rnum == 3 and next time $rnum == 106 then $rnum == 10 and so on...
But I would rather have a bias range of say 80 - 120 so it would more likely select a number within the bias range than outside of it.
$rnum == 86 and next time $rnum == 112 then $rnum == 93 but still be able to $rnum == 24 and so on...
I thought I might have been able to do this:
if($rnum < $middle_of_bias){
$rnum++;
}else{
$rnum--;
}
but didn't really work, as you can see if $rnum == 1 after applying the bias it would only make it 2 not making this method very successful.
Thanks for your help.
Edit: everyones answers were great. I really liked gnur's one and Phil H's I have made my modifications to rmflow's one and it is working how I wanted it to. Thanks to every one that helped!
if (rand(0, 10) > 2)
{
$rnum = rand($low, $high);
}else{
$rnum = rand(0, $max);
}
if (rand(0, 10) > 7)
{
$rnum = rand(80, 120);
}
else
{
$rnum = rand(0, 200);
}
You probably want to use rand() to get a random number from a larger range in a uniform distribution and then use a function to map that larger range to the smaller range you actually want in a way that produces the distribution you want.
Another possibility to bias your random number:
$rnum = rand(90,110);
$rex1 = 45 - rand(0,90);
$rex2 = 45 - rand(0,90);
$rbias = $rnum + $rex1 + rex2;
This will increase likeliness of numbers around 100, numbers of 0-10 and 190-200 are quite unlikely while numbers between 80-120 are very likely.
create two ranges, one biased, one not.
not biased range: 0, 200
biased range: 80, 120
then pick a likeliness for getting a none biased number, say 20%. that's one 5th, or, 1 out of 5. then generate a random number between 1 and 5, if it comes out as 1, generate a random number from the none biased range. otherwise generate a number from the biased range.
since i started writing, a couple of answers has come in. the answer from rmflow describes my flow, but with a 36% likeliness of getting a none biased number.
good luck! :)
Just add spare capacity to the end of the range and anything above the maximum you shift into the biased range.
$rnum = rand(0,240);
if($rnum > 200) {
$rnum -= 120; // 201 -> 81
}
This will only double the probability of getting a value in that range. If you want more bias, extend the range further:
$rmax = 200;
$bmin = 80;
$bmax = 120;
$brange = $bmax - $bmin;
$nbias = 4;
$rnum = rand(0,$rmax+($brange*$nbias));
if($rnum > $rmax) {
$excess -= $rmax; // 201 -> 81
$remainder = modulo($rnum,$brange);
$rnum = $remainder+$bmin;
}
You can do it by defining an array of $item=>$weight:
$biasedArray = array(
'Blue' => 50,
'Yellow' => 30,
'Pink' => 10,
'Green' => 10,
);
chooseFromBiasedArray($biasArray);
function chooseFromBiasedArray($biasArray) {
$totalWeight = array_sum($biasedArray);
$randChoice = rand(1,$totalWeight);
$currentWeight = 0;
reset($biasedArray);
while ($currentWeight < $randChoice) {
$currentKey = key($biasedArray);
$currentWeight += $biasedArray[$currentKey];
if ($currentWeight < $randChoice) {
next($biasedArray);
}
}
//echo $randChoice . " -> " . current($biasArray);
return current($biasArray);
}
I wrote it very fast, you can do it much cleaner but the idea will be the same.
The answer by #rmflow would be the way in which I would add a weighted bias to a range. However, I would use the mt_rand function for better randomness.
mt_rand — Generate a random value via the Mersenne Twister Random Number Generator.
It is also purported to be 4x faster (This function produces a better random value, and is 4 times faster than rand()), however, in my experience I see little only a 20% increase in performance.
if (mt_rand(0, 10) > 7)
{
$rnum = mt_rand(80, 120);
}
else
{
$rnum = mt_rand(0, 200);
}
Related
How does one generate a random float between 0 and 1 in PHP?
I'm looking for the PHP's equivalent to Java's Math.random().
You may use the standard function: lcg_value().
Here's another function given on the rand() docs:
// auxiliary function
// returns random number with flat distribution from 0 to 1
function random_0_1()
{
return (float)rand() / (float)getrandmax();
}
Example from documentation :
function random_float ($min,$max) {
return ($min+lcg_value()*(abs($max-$min)));
}
rand(0,1000)/1000 returns:
0.348 0.716 0.251 0.459 0.893 0.867 0.058 0.955 0.644 0.246 0.292
or use a bigger number if you want more digits after decimal point
class SomeHelper
{
/**
* Generate random float number.
*
* #param float|int $min
* #param float|int $max
* #return float
*/
public static function rand($min = 0, $max = 1)
{
return ($min + ($max - $min) * (mt_rand() / mt_getrandmax()));
}
}
update:
forget this answer it doesnt work wit php -v > 5.3
What about
floatVal('0.'.rand(1, 9));
?
this works perfect for me, and it´s not only for 0 - 1 for example between 1.0 - 15.0
floatVal(rand(1, 15).'.'.rand(1, 9));
function mt_rand_float($min, $max, $countZero = '0') {
$countZero = +('1'.$countZero);
$min = floor($min*$countZero);
$max = floor($max*$countZero);
$rand = mt_rand($min, $max) / $countZero;
return $rand;
}
example:
echo mt_rand_float(0, 1);
result: 0.2
echo mt_rand_float(3.2, 3.23, '000');
result: 3.219
echo mt_rand_float(1, 5, '00');
result: 4.52
echo mt_rand_float(0.56789, 1, '00');
result: 0.69
$random_number = rand(1,10).".".rand(1,9);
function frand($min, $max, $decimals = 0) {
$scale = pow(10, $decimals);
return mt_rand($min * $scale, $max * $scale) / $scale;
}
echo "frand(0, 10, 2) = " . frand(0, 10, 2) . "\n";
This question asks for a value from 0 to 1. For most mathematical purposes this is usually invalid albeit to the smallest possible degree. The standard distribution by convention is 0 >= N < 1. You should consider if you really want something inclusive of 1.
Many things that do this absent minded have a one in a couple billion result of an anomalous result. This becomes obvious if you think about performing the operation backwards.
(int)(random_float() * 10) would return a value from 0 to 9 with an equal chance of each value. If in one in a billion times it can return 1 then very rarely it will return 10 instead.
Some people would fix this after the fact (to decide that 10 should be 9). Multiplying it by 2 should give around a ~50% chance of 0 or 1 but will also have a ~0.000000000465% chance of returning a 2 like in Bender's dream.
Saying 0 to 1 as a float might be a bit like mistakenly saying 0 to 10 instead of 0 to 9 as ints when you want ten values starting at zero. In this case because of the broad range of possible float values then it's more like accidentally saying 0 to 1000000000 instead of 0 to 999999999.
With 64bit it's exceedingly rare to overflow but in this case some random functions are 32bit internally so it's not no implausible for that one in two and a half billion chance to occur.
The standard solutions would instead want to be like this:
mt_rand() / (getrandmax() + 1)
There can also be small usually insignificant differences in distribution, for example between 0 to 9 then you might find 0 is slightly more likely than 9 due to precision but this will typically be in the billionth or so and is not as severe as the above issue because the above issue can produce an invalid unexpected out of bounds figure for a calculation that would otherwise be flawless.
Java's Math.random will also never produce a value of 1. Some of this comes from that it is a mouthful to explain specifically what it does. It returns a value from 0 to less than one. It's Zeno's arrow, it never reaches 1. This isn't something someone would conventionally say. Instead people tend to say between 0 and 1 or from 0 to 1 but those are false.
This is somewhat a source of amusement in bug reports. For example, any PHP code using lcg_value without consideration for this may glitch approximately one in a couple billion times if it holds true to its documentation but that makes it painfully difficult to faithfully reproduce.
This kind of off by one error is one of the common sources of "Just turn it off and on again." issues typically encountered in embedded devices.
Solution for PHP 7. Generates random number in [0,1). i.e. includes 0 and excludes 1.
function random_float() {
return random_int(0, 2**53-1) / (2**53);
}
Thanks to Nommyde in the comments for pointing out my bug.
>>> number_format((2**53-1)/2**53,100)
=> "0.9999999999999998889776975374843459576368331909179687500000000000000000000000000000000000000000000000"
>>> number_format((2**53)/(2**53+1),100)
=> "1.0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000"
Most answers are using mt_rand. However, mt_getrandmax() usually returns only 2147483647. That means you only have 31 bits of information, while a double has a mantissa with 52 bits, which means there is a density of at least 2^53 for the numbers between 0 and 1.
This more complicated approach will get you a finer distribution:
function rand_754_01() {
// Generate 64 random bits (8 bytes)
$entropy = openssl_random_pseudo_bytes(8);
// Create a string of 12 '0' bits and 52 '1' bits.
$x = 0x000FFFFFFFFFFFFF;
$first12 = pack("Q", $x);
// Set the first 12 bits to 0 in the random string.
$y = $entropy & $first12;
// Now set the first 12 bits to be 0[exponent], where exponent is randomly chosen between 1 and 1022.
// Here $e has a probability of 0.5 to be 1022, 0.25 to be 1021, etc.
$e = 1022;
while($e > 1) {
if(mt_rand(0,1) == 0) {
break;
} else {
--$e;
}
}
// Pack the exponent properly (add four '0' bits behind it and 49 more in front)
$z = "\0\0\0\0\0\0" . pack("S", $e << 4);
// Now convert to a double.
return unpack("d", $y | $z)[1];
}
Please note that the above code only works on 64-bit machines with a Litte-Endian byte order and Intel-style IEEE754 representation. (x64-compatible computers will have this). Unfortunately PHP does not allow bit-shifting past int32-sized boundaries, so you have to write a separate function for Big-Endian.
You should replace this line:
$z = "\0\0\0\0\0\0" . pack("S", $e << 4);
with its big-endian counterpart:
$z = pack("S", $e << 4) . "\0\0\0\0\0\0";
The difference is only notable when the function is called a large amount of times: 10^9 or more.
Testing if this works
It should be obvious that the mantissa follows a nice uniform distribution approximation, but it's less obvious that a sum of a large amount of such distributions (each with cumulatively halved chance and amplitude) is uniform.
Running:
function randomNumbers() {
$f = 0.0;
for($i = 0; $i < 1000000; ++$i) {
$f += \math::rand_754_01();
}
echo $f / 1000000;
}
Produces an output of 0.49999928273099 (or a similar number close to 0.5).
I found the answer on PHP.net
<?php
function randomFloat($min = 0, $max = 1) {
return $min + mt_rand() / mt_getrandmax() * ($max - $min);
}
var_dump(randomFloat());
var_dump(randomFloat(2, 20));
?>
float(0.91601131712832)
float(16.511210331931)
So you could do
randomFloat(0,1);
or simple
mt_rand() / mt_getrandmax() * 1;
what about:
echo (float)('0.' . rand(0,99999));
would probably work fine... hope it helps you.
My task:
Generate random numbers between 1 and 20, to 1 decimal place.
However my issue as simple as mt_rand. I want most of the numbers generated to be lower around 0.5 - 4.5 with the occasional number being between 4.5-10 and very rarely say once every 12-20 hours being between 10-20.
I've been using the following but have no idea where to go from. I am a very basic self-taught programmer.
$min = 1;
$max = 20;
$suisse_interest = mt_rand ($min*10, $max*10) / 10
Maybe if I briefly explain why I want this it may help..
I own an online game and want to add 3 "banks" with each bank generating different interests each hour. Most of the time I want it low, but sometimes higher and very rarely very high (15-20%).
With the above code the random number goes too high to often.
Any help with this is greatly appreciated!
You need an exponential calculation. If you use a function similar to the following function, the probability for low numbers increases. Of course you need to adapt the numbers a bit to provide an output suiting your needs.
$i = 0;
while($i<30) {
$i++;
$rand = mt_rand(0, 7000) / 100; // 0.0 ~ 70.0
// This is the most important line:
$output = round( 20*(pow(0.95,$rand)) , 1);
echo "$output ";
}
Sample output:
1.8 4.3 2.6 5.5 3.7 15.5 1.6 0.6 0.6 1.6 5.8
1.3 6.1 3.2 0.8 1.7 14.7 7.9 1.3 10.3 5.5 12.6
1.5 8.4 1.5 0.9 13.3 5.8 7.5 1.7
As you see, mostly smaller number are printed.
The probability to get 20 is around 1.4% in my code whereas the probability to get a number smaller than 5 is around 78%
Try this.The probability to 1.0~4.5 is around 96%, 4.5~10.0 is around 2%, and 10.0~20.0 is around 2%.
<?php
// 1.0~4.5 96%
// 4.5~10.0 2%
// 10.0~20.0 2%
function fun() {
$num = mt_rand(1, 100);
if ($num > 0 && $num <= 96) {
$return = mt_rand(10, 45) / 10; // 96%
} else if ($num > 96 && $num <= 98) {
$return = mt_rand(45, 100) / 10; // 2%
} else {
$return = mt_rand(100, 200) / 10; // 2%
}
return sprintf("%01.1f",$return);
}
echo fun();
?>
This is not a PHP-specific problem.
What you need is a non-linear probability law, that you can then implement in PHP.
If you want something centered around an average value, the ideal would be a gaussian aka normal distribution, but computing it requires various complicated tricks, most of them being optimized for rapid generation at the cost of increasing complexity.
If you generate only a few values each hour, performance will not be an issue.
A reasonable approximation would be to sum 3 or 4 random variables, taking advantage of the central limit theorem.
Summing random values between 0 and twice your middle rate will create an approximation of a gaussian centered around your middle value.
You can then clamp values inferior to the middle point if you don't want low rates. The net result would be 50% chances of getting middle rate and a steadily decreasing chance to get up to twice that value.
An increasing number of sums will "narrow" the curve, making it less likely to get a high value.
for instance:
define ("INTEREST_MEAN", 10);
define ("INTEREST_SPREAD", 5);
function random_interest ()
{
$res = 0;
for ($i = 0 ; $i != INTEREST_SPREAD ; $i++) $res += mt_rand(0, 2*INTEREST_MEAN);
$res /= INTEREST_SPREAD; // normalize the sum to get a mean-centered result
$res = max ($res, INTEREST_MEAN); // clamp lower values
}
I'm not sure if this title is correct but here's basically what I am trying to do.
I am trying to check if a number is less than 100 and if it isn't I would like to know what factor of 10 I need to divide it by to get below 100 i.e. for 7923 the factor is 100 to make it 79.23 and for 452,936,489 the factor would be 10,000,000 to make it 45.2936489.
Is there a function or a piece of script that does that out there?
Cheers
$number = 452936489;
$factor = pow(10, ceil(log($number/100) / log(10)));
Ok. basic math:
you need to find a power of 10 divisor that reduces your number below 100, so the log business figures out the exact fractional power of 10 required to turn 10 into your original number. That comes out to be around 6.6560373....
That gets rounded up to 7, and is then used to raise 10 to that power.
10^7 = 10,000,000
452936489 / 10^7 = 45.2936489
<?
$num = 7923;
$x = 10;
while(true)
{
$result = $num/$x;
if($result < 100)
{
die($x."");
}
else
{
$x *= 10;
}
}
?>
How does one generate a random float between 0 and 1 in PHP?
I'm looking for the PHP's equivalent to Java's Math.random().
You may use the standard function: lcg_value().
Here's another function given on the rand() docs:
// auxiliary function
// returns random number with flat distribution from 0 to 1
function random_0_1()
{
return (float)rand() / (float)getrandmax();
}
Example from documentation :
function random_float ($min,$max) {
return ($min+lcg_value()*(abs($max-$min)));
}
rand(0,1000)/1000 returns:
0.348 0.716 0.251 0.459 0.893 0.867 0.058 0.955 0.644 0.246 0.292
or use a bigger number if you want more digits after decimal point
class SomeHelper
{
/**
* Generate random float number.
*
* #param float|int $min
* #param float|int $max
* #return float
*/
public static function rand($min = 0, $max = 1)
{
return ($min + ($max - $min) * (mt_rand() / mt_getrandmax()));
}
}
update:
forget this answer it doesnt work wit php -v > 5.3
What about
floatVal('0.'.rand(1, 9));
?
this works perfect for me, and it´s not only for 0 - 1 for example between 1.0 - 15.0
floatVal(rand(1, 15).'.'.rand(1, 9));
function mt_rand_float($min, $max, $countZero = '0') {
$countZero = +('1'.$countZero);
$min = floor($min*$countZero);
$max = floor($max*$countZero);
$rand = mt_rand($min, $max) / $countZero;
return $rand;
}
example:
echo mt_rand_float(0, 1);
result: 0.2
echo mt_rand_float(3.2, 3.23, '000');
result: 3.219
echo mt_rand_float(1, 5, '00');
result: 4.52
echo mt_rand_float(0.56789, 1, '00');
result: 0.69
$random_number = rand(1,10).".".rand(1,9);
function frand($min, $max, $decimals = 0) {
$scale = pow(10, $decimals);
return mt_rand($min * $scale, $max * $scale) / $scale;
}
echo "frand(0, 10, 2) = " . frand(0, 10, 2) . "\n";
This question asks for a value from 0 to 1. For most mathematical purposes this is usually invalid albeit to the smallest possible degree. The standard distribution by convention is 0 >= N < 1. You should consider if you really want something inclusive of 1.
Many things that do this absent minded have a one in a couple billion result of an anomalous result. This becomes obvious if you think about performing the operation backwards.
(int)(random_float() * 10) would return a value from 0 to 9 with an equal chance of each value. If in one in a billion times it can return 1 then very rarely it will return 10 instead.
Some people would fix this after the fact (to decide that 10 should be 9). Multiplying it by 2 should give around a ~50% chance of 0 or 1 but will also have a ~0.000000000465% chance of returning a 2 like in Bender's dream.
Saying 0 to 1 as a float might be a bit like mistakenly saying 0 to 10 instead of 0 to 9 as ints when you want ten values starting at zero. In this case because of the broad range of possible float values then it's more like accidentally saying 0 to 1000000000 instead of 0 to 999999999.
With 64bit it's exceedingly rare to overflow but in this case some random functions are 32bit internally so it's not no implausible for that one in two and a half billion chance to occur.
The standard solutions would instead want to be like this:
mt_rand() / (getrandmax() + 1)
There can also be small usually insignificant differences in distribution, for example between 0 to 9 then you might find 0 is slightly more likely than 9 due to precision but this will typically be in the billionth or so and is not as severe as the above issue because the above issue can produce an invalid unexpected out of bounds figure for a calculation that would otherwise be flawless.
Java's Math.random will also never produce a value of 1. Some of this comes from that it is a mouthful to explain specifically what it does. It returns a value from 0 to less than one. It's Zeno's arrow, it never reaches 1. This isn't something someone would conventionally say. Instead people tend to say between 0 and 1 or from 0 to 1 but those are false.
This is somewhat a source of amusement in bug reports. For example, any PHP code using lcg_value without consideration for this may glitch approximately one in a couple billion times if it holds true to its documentation but that makes it painfully difficult to faithfully reproduce.
This kind of off by one error is one of the common sources of "Just turn it off and on again." issues typically encountered in embedded devices.
Solution for PHP 7. Generates random number in [0,1). i.e. includes 0 and excludes 1.
function random_float() {
return random_int(0, 2**53-1) / (2**53);
}
Thanks to Nommyde in the comments for pointing out my bug.
>>> number_format((2**53-1)/2**53,100)
=> "0.9999999999999998889776975374843459576368331909179687500000000000000000000000000000000000000000000000"
>>> number_format((2**53)/(2**53+1),100)
=> "1.0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000"
Most answers are using mt_rand. However, mt_getrandmax() usually returns only 2147483647. That means you only have 31 bits of information, while a double has a mantissa with 52 bits, which means there is a density of at least 2^53 for the numbers between 0 and 1.
This more complicated approach will get you a finer distribution:
function rand_754_01() {
// Generate 64 random bits (8 bytes)
$entropy = openssl_random_pseudo_bytes(8);
// Create a string of 12 '0' bits and 52 '1' bits.
$x = 0x000FFFFFFFFFFFFF;
$first12 = pack("Q", $x);
// Set the first 12 bits to 0 in the random string.
$y = $entropy & $first12;
// Now set the first 12 bits to be 0[exponent], where exponent is randomly chosen between 1 and 1022.
// Here $e has a probability of 0.5 to be 1022, 0.25 to be 1021, etc.
$e = 1022;
while($e > 1) {
if(mt_rand(0,1) == 0) {
break;
} else {
--$e;
}
}
// Pack the exponent properly (add four '0' bits behind it and 49 more in front)
$z = "\0\0\0\0\0\0" . pack("S", $e << 4);
// Now convert to a double.
return unpack("d", $y | $z)[1];
}
Please note that the above code only works on 64-bit machines with a Litte-Endian byte order and Intel-style IEEE754 representation. (x64-compatible computers will have this). Unfortunately PHP does not allow bit-shifting past int32-sized boundaries, so you have to write a separate function for Big-Endian.
You should replace this line:
$z = "\0\0\0\0\0\0" . pack("S", $e << 4);
with its big-endian counterpart:
$z = pack("S", $e << 4) . "\0\0\0\0\0\0";
The difference is only notable when the function is called a large amount of times: 10^9 or more.
Testing if this works
It should be obvious that the mantissa follows a nice uniform distribution approximation, but it's less obvious that a sum of a large amount of such distributions (each with cumulatively halved chance and amplitude) is uniform.
Running:
function randomNumbers() {
$f = 0.0;
for($i = 0; $i < 1000000; ++$i) {
$f += \math::rand_754_01();
}
echo $f / 1000000;
}
Produces an output of 0.49999928273099 (or a similar number close to 0.5).
I found the answer on PHP.net
<?php
function randomFloat($min = 0, $max = 1) {
return $min + mt_rand() / mt_getrandmax() * ($max - $min);
}
var_dump(randomFloat());
var_dump(randomFloat(2, 20));
?>
float(0.91601131712832)
float(16.511210331931)
So you could do
randomFloat(0,1);
or simple
mt_rand() / mt_getrandmax() * 1;
what about:
echo (float)('0.' . rand(0,99999));
would probably work fine... hope it helps you.
I need to generate a random number.
But the twist is that the % of lower numbers should be greater than the higher.
For example.
rand > 1 to 100
13,
15,
12,
16,
87,
15,
27,
12,
1,
12,
98,
12,
53,
12,
14....
The bold integers will be the ones return from the 1-100 range.
The math should be like so rand = a number lower than max/2
Hope you guys can help.
Ps, How would a matrix come into this ? im not superior at maths :(
The abs answer seems to be the one.
$up = $down = 0;
while(true)
{
if(abs((rand()%150)-50) < 50)
{
$up++;
}else
{
$down++;
}
if( ($up + $down) == 500){ break;}
}
echo $up . '/' . $down;
how about
n = abs((rand()%150)-50)
$x = rand(0,1) ? rand(1,100) : rand(1,50);
Simple method: the first rand(0,1) selects between the two cases. Either 1..50 or 1..100 as random range. Since 1,100 already encompases 1,50, the latter range is selected 100% of the time, the former case only in 1 of 2 runs.
If you want a distribution where the highest numer 99 gets selected almost never, but the lower numbers 1..20 pretty frequent, then a simple rand(1,rand(1,100)) would do.
$rand = (rand() * rand()) / (getrandmax() * getrandmax());
This will give you a random number between 0 and 1 with a high probability of falling into the lower end of the range and the probability of a larger number decreasing exponentially. You can then scale this result to any range that you want.