4

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!

2
  • possible duplicate of Defining percentage for random number Commented Feb 22, 2015 at 9:25
  • Since the distribution you want to create is monotone decreasing, you can use the ziggurat algorithm to map your distribution (uniform) onto the one you want (which you can arbitrarily create). Or you could use the box muller transformation, but it's less efficient (and since you're doing this a lot, you might want to precompute the tables described in the ziggurat algorithm). Commented Feb 22, 2015 at 10:49

3 Answers 3

3

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.

Chart

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%

Sign up to request clarification or add additional context in comments.

1 Comment

Perfect, this and the other answer both worked. Exactly what I was after! Thanks!
2

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();
?>

3 Comments

Thank you so much, this has helped massively. It all makes a lot more sense now!
Once more question if you don't mind. How can I take the echo fun() and put it into my database. I know the below won't make sense but maybe it will make my goal clearer.. $interest = (the result of fun()) mysql_query("UPDATE r$game[0]_$tab[pimp] SET suisseint=$interest WHERE id=$user1no[id];"); Thank you!
Sorry for late because of the Spring Festival. Your SQL is right, check the table structure and try again.
0

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
}

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.