In PHP, how do I generate a big pseudo-random number?
Asked Answered
N

14

16

I'm looking for a way to generate a big random number with PHP, something like:

mt_rand($lower, $upper);

The closer I've seen is gmp_random() however it doesn't allow me to specify the lower and upper boundaries only the number of bits per limb (which I've no idea what it is).

EDIT: Axsuuls answer seems to be pretty close to what I want and very similar to gmp_random however there seems to be only one flaw in one scenario.

Suppose I wan't to get a random number between:

  • 1225468798745475454898787465154

and:

  • 1225468798745475454898787465200

So if the function is called BigRandomNumber():

BigRandomNumber($length = 31);

This can easily return 9999999999999999999999999999999 which is out of the specified boundary.

How can I use a min / max boundary instead of a length value?

BigRandomNumber('1225468798745475454898787465154', '1225468798745475454898787465200');

This should return a random number between 1225468798745475454898787465 [154 .. 200].

For the reference I believe the solution might have to make use of the function supplied in this question.

EDIT: The above post was deleted, here it is:

function compare($number1, $operator, $number2) {
  $x = bccomp($number1, $number2);

  switch($operator) {
    case '<':
      return -1===$x;
    case '>':
      return 1===$x;
    case '=':
    case '==':
    case '===':
      return 0===$x;
    case '!=':
    case '!==':
    case '<>':
      return 0!==$x;
  }
}
Nonviolence answered 25/9, 2009 at 22:23 Comment(3)
Are you looking for a random number or a random digit string? .. What do you plan to use the result of this function call for? if the values you'd like are > PHP_INT_MAX then manipulating it becomes an issue..Thorpe
Your last link ("function supplied in this question") is broken.Memoried
It seems to me, a function like the one the question describes would be very useful for anyone needing to generate random large numbers. I decided to use the function given as the accepted answer in my application, but before doing so I ran some tests to measure its performance. It performed quite well, and I'd be happy to share the results. I'll first try to add them by editing the accepted answer.Memoried
E
17

Try the following:

function BigRandomNumber($min, $max) {
  $difference   = bcadd(bcsub($max,$min),1);
  $rand_percent = bcdiv(mt_rand(), mt_getrandmax(), 8); // 0 - 1.0
  return bcadd($min, bcmul($difference, $rand_percent, 8), 0);
}

The math is as following: multiply the difference between the minimum and maximum by a random percentage, and add to the minimum (with rounding to an int).

Edifice answered 1/10, 2009 at 15:31 Comment(4)
With your approach, there will only be some 100 million possibilities.Phago
So increase the precision to 16. Really, it's the only effective way to generate 1 random number and then "scale" it into the proper range. I'm not a statistician.Edifice
To mimic mt_rand() funcionality bcsub($max,$min); should also be bcadd(bcsub($max, $min), 1);Nonviolence
This code broke my production and I was trying to figure it out for 8 hours what the hack is wrong with it. after all this hours I found out this will produce max number 100 million even if the $max is greater than that. In PHP 7.0 use: random_int.Exoenzyme
B
7

What you really need to know is the relative gap; if it's small then you can generate a number from 0 to the maximum gap then add the minimum to that.

Beograd answered 3/10, 2009 at 3:58 Comment(1)
This is the most brilliant answer here.Boutte
T
3

This will give you more zeros in your giant random number and you can also specify the length of the giant random number (can your giant random number start with a 0? if not, that can also be easily implemented)

<?php

$randNumberLength = 1000;  // length of your giant random number
$randNumber = NULL;

for ($i = 0; $i < $randNumberLength; $i++) {
    $randNumber .= rand(0, 9);  // add random number to growing giant random number

}

echo $randNumber;

?>

Good luck!

Traceable answered 25/9, 2009 at 22:51 Comment(3)
You can cast to int at the end to get rid of any amonut of left zeroes.Baltazar
@Vinko, if you cast the random number to a int, you'll get the number in scientific notation.Nonviolence
@Axsuul: This is a fine approach however, I want to specify the upper and lower boundary of the numbers instead of the number length how would I do that?Nonviolence
C
1

What you can do is create a few smaller random numbers and combine them. Not sure on how large you actually need though.

Confectionery answered 25/9, 2009 at 22:26 Comment(7)
I had the same idea, not sure how random the generated number would be though.Nonviolence
It would be almost as random as the smaller random numbers.Cicatrix
Aye it's almost as random, the main issue is that none of the numbers will start with zero. So zeros will be less common in some rare cases.Bicapsular
You can combine random numbers perfectly fine. Given the assumption of randomness, you don't lose anything in randomness even when combining them. Simply concatenating them bitwise should suffice. You can take a look at Java's java.util.Random.nextDouble() for example, which simply builds a 53-bit random number from two smaller ones.Inclinometer
It really depends on your application - I wouldn't use a PRNG that's an addition of 2 PRNGs for crypto or simulations because of possible skew problems as Olafur pointed out.Sighted
Calyth: this is for a probability object, and I'm not dealing with anything sensitive here.Nonviolence
The generated numbers are not equally distributed over the range (min-max). This can be easily verified by analyzing the generation of a random number between 0 and 2 by adding random numbers between 0 and 1. The probabilities would be 25% for 0 (two zeros), 50% for 1 (0 and 1 or 1 and 0) and 25% for 2 (two ones).Maltase
M
1

Big Integers are from 1-20 range. So generate a number using mt_rand($lower, $upper) and multiply with another mt_rand($lower, $upper)

$N1=mt_rand($lower, $upper);
$N2=mt_rand($lower, $upper);
$N3=$N1*N2;

Note:multiplication of n-digits with n-digits gives you n*2 digits.

Mckeown answered 7/2, 2020 at 11:10 Comment(1)
This way you can not get bigger output than max bigint value isEpagoge
G
0
$lower = gmp_com("1225468798745475454898787465154");
$upper = gmp_com("1225468798745475454898787465200");

$range_size = gmp_sub($upper, $lower);

$rand = gmp_random(31);
$rand = gmp_mod($rand, $range_size);

$result = gmp_add($rand, $lower);

Totally untested :-)

Godsey answered 1/10, 2009 at 15:36 Comment(0)
G
0

This might work for you. (I am not sure why you need it, so it might not be the best way to do it, but it should fit your requirements):

<?php
function bigRandomNumber($min, $max)
{
 // check input first
    if ($max < $min) { return false; }
    // Find max & min length of the number
    $lenMin = strlen ($min);
    $lenMax = strlen ($max);

    // Generate a random length for the random number
    $randLen = $lenMin + mt_rand(0, $lenMax - $lenMin);
    /* Generate the random number digit by digit, 
       comparing it with the min and max values */
 $b_inRange = false;
    for ($i = 0; $i < $randLen; $i++)
 {
  $randDigit = mt_rand(0,9);

  /* As soon as we are sure that the number will stay 
          in range, we can stop comparing it to min and max */
  if (!$b_inRange)
  {
   $tempRand = $rand . $randDigit;
   $tempMin = substr($min, 0, $i+1);
   $tempMax = substr($max, 0, $i+1);
   // Make sure that the temporary random number is in range
   if ($tempRand < $tempMin || $tempRand > $tempMax)
   {
    $lastDigitMin = substr($tempMin, -1);
    $lastDigitMax = substr($tempMax, -1);
    $tempRand = $rand . @mt_rand($lastDigitMin, $lastDigitMax);
   }
   /* Check if $tempRand is equal to the min or to the max value. 
               If it is not equal, then we know it will stay in range */
   if ($tempRand > $tempMin && $tempRand < $tempMax)
   {
    $b_inRange = true;
   }
  }
  else
  {
   $tempRand = $rand . $randDigit;
  }
  $rand = $tempRand;  
 }
 return $rand;
}

I tried a couple times and it looks like it works OK. Optimize if needed. The idea is to start by figuring out a random length for your random number that would put it in the acceptable range. Then generate random digits one by one up to that length by concatenating. If it is not in range, generate a new random digit in range and concatenate.

I use the fact that PHP will convert a string to a number to take advantage of the string functions. Of course this generates a warning for mt_rand, but as we use only numbers, it should be safe to suppress it.

Now, I have to say that I am quite curious as to why you need this in the first place.

Gist answered 3/10, 2009 at 8:4 Comment(0)
V
0
/* Inputs: 
 * min - GMP number or string: lower bound
 * max - GMP number or string: upper bound
 * limiter - GMP number or string: how much randomness to use.
 *  this value is quite obscure (see `gmp_random`, but the default
 *  supplies several hundred bits of randomness, 
 *  which is probably enough.
 * Output: A random number between min (inclusive) and max (exclusive).
*/
function BigRandomNumber($min, $max, $limiter = 20) {
  $range = gmp_sub($max, $min);
  $random = gmp_random();
  $random = gmp_mod($random, $range);
  $random = gmp_add($min, $random);
  return $random;
}

This is just the classic formula rand_range($min, $max) = $min + rand() % ($max - $min) translated to arbitrary-precision arithmetic. It can exhibit a certain amount of bias if $max - $min isn't a power of two, but if the number of bits of randomness is high enough compared to the size of $max - $min the bias becomes negligible.

Valley answered 3/10, 2009 at 10:49 Comment(0)
J
0

This may work:

  • Split the number into an array with 9 numbers or less ("the rest") ... 9 chars because max rand number is 2147483647 on my machine.
  • For each "9-or-less numbers array block", create a random number.
  • Implode the array and you will now have a usable random number.

Example code that illustrates the idea (notice: the code is undone)

function BigRandomNumber($min,$max) {
// Notice: Will only work when both numbers have same length.
echo (strlen($min) !== strlen($max)) ? "Error: Min and Max numbers must have same length" : NULL;
$min_arr = str_split($min);
$max_arr = str_split($max);
// TODO: This loop needs to operate on 9 chars ($i will increment by $i+9)
for($i=0; $i<=count($max_arr); $i++) {
    if($i == 0) {
        // First number: >=first($min) and <=first($max).
        $new_arr[$i] = rand( $min_arr[0], $max_arr[0]);
    } else if($i == count($max_arr)) {
        // Last number <= $max .. not entirely correct, feel free to correct it.
        $new_arr[$i] = rand(0, substr($max,-1));
    } else {
        $new_arr[$i] = rand(0,9);
    }
}
return implode($new_arr);
}
Jejunum answered 3/10, 2009 at 17:1 Comment(0)
Z
0

Tested and works

<?php 

$min = "1225468798745475454898787465154";
$max = "1225468798745475454898787465200";

$bigRandNum = bigRandomNumber($min,$max);
echo "The Big Random Number is: ".$bigRandNum."<br />";

function bigRandomNumber($min,$max) {
    // take the max number length
    $number_length = strlen($max);

    // Set the counter
    $i = 1;

    // Find the base and the min and max ranges
    // Loop through the min to find the base number
    while ($i <= $number_length) {
        $sub_string = substr($min, 0, $i);

        // format pattern
        $format_pattern = '/'.$sub_string.'/';
        if (!preg_match($format_pattern, $max)) {
            $base = $sub_string;

            // Set the min and max ranges
            $minRange = substr($min, ($i - 1), $number_length);
            $maxRange = substr($max, ($i - 1), $number_length);

            // End while loop, we found the base
            $i = $number_length;
        }
        $i++;
    }
    // find a random number with the min and max range
    $rand = rand($minRange, $maxRange);

    // add the base number to the random number
    $randWithBase = $base.$rand;

    return $randWithBase;
}

?>
Zeebrugge answered 5/10, 2009 at 12:54 Comment(0)
E
0

Generating 'n' random chars is not really an option, as random('9999999999') could still, theoretically, return 1...

Here's a quite simple function:

function bcrand($max) { 
    return bcmul($max, (string)mt_rand() / mt_getrandmax() ); 
}

Note that it will NOT return N bits of randomness, just adjust scale

Elsewhere answered 9/3, 2013 at 2:25 Comment(0)
E
0

This question was asked like decade ago but for folks that come from Google propper answer is gmp_random_range("min_value", "max_value") funciton.

$range=gmp_random_range("1225468798745475454898787465154", "1225468798745475454898787465200");
echo gmp_strval($range); // displays value as string
Epagoge answered 8/4, 2020 at 2:3 Comment(0)
P
-1

Take your floor and and your random number in the range to it.

1225468798745475454898787465154 + rand(0, 6)
Pursuivant answered 1/10, 2009 at 15:43 Comment(0)
P
-1

Here is pseudocode:


// generate a random number between N1 and N2

rangesize = N2 - N1 + 1
randlen = length(rangesize) + 4 // the 4 is to get more digits to reduce bias
temp = BigRandomNumber(randlen) // generate random number, "randlen" digits long
temp = temp mod rangesize
output N1 + temp

Notes:

  • all arithmetic here (except in the second line) must be arbitrary precision: use the bcmath library for this
  • in the second line, "length" is number of digits, so the "length" of 1025 would be 4
Phago answered 3/10, 2009 at 3:39 Comment(2)
BigRandomNumber() is the function that is missing. Also, you assume that the rangesize will be smaller than the upper limit of the BigRandomNumber(). That assumption might not work if the random generator of BigRandomNumber() is mt_rand(), and if it isn't, then you have to write it, which is what this question is about.Gist
The BigRandomNumber function in my answer refers to Axsuul's answer.Phago

© 2022 - 2024 — McMap. All rights reserved.