В PHP, как я могу создать большой псевдо-случайных чисел?


Я ищу способ генерировать большое случайное число с помощью PHP, что-то вроде:

mt_rand($lower, $upper);

Чем ближе я видел, тем gmp_random () однако он не позволяет мне указывать нижнюю и верхнюю границы только количество битов на Лимб (что я понятия не имею, что это такое).

EDIT: Axsuuls ответ, кажется, довольно близок к тому, что я хочу и очень похож на gmp_random, однако, кажется, есть только один недостаток в одном сценарии.

Предположим, что я не хочу чтобы получить случайное число между:

  • 1225468798745475454898787465154

И:

  • 1225468798745475454898787465200

Таким образом, если функция называется BigRandomNumber():

BigRandomNumber($length = 31);

Это может легко вернуть 99999999999999999999999999999, который находится вне указанной границы.

Как я могу использовать границу min / max вместо значения длины?

BigRandomNumber('1225468798745475454898787465154', '1225468798745475454898787465200');

Это должно возвращать случайное число между 1225468798745475454898787465 [154 .. 200] .

Для справки я полагаю, что решение, возможно, придется использовать функцию , поставленную в этом вопросе.

EDIT: вышеуказанный пост был удален, вот он:

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;
  }
}
12 13

12 ответов:

Попробуйте следующее:

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);
}
Математика выглядит следующим образом: умножьте разницу между минимумом и максимумом на случайный процент и добавьте к минимуму (с округлением до int).

Что вам действительно нужно знать, так это относительный зазор; если он мал, то вы можете сгенерировать число от 0 до максимального зазора, а затем добавить к нему минимальный.

Это даст вам больше нулей в вашем гигантском случайном числе, и вы также можете указать длину гигантского случайного числа (может ли ваше гигантское случайное число начинаться с 0? если нет, то это также может быть легко реализовано)

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

?>

Удачи!

Что вы можете сделать, так это создать несколько случайных чисел меньшего размера и объединить их. Хотя не уверен, насколько большой вам на самом деле нужен.

$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);

Полностью непроверено : -)

Это может сработать для вас. (Я не уверен, зачем вам это нужно, так что это может быть не лучший способ сделать это, но он должен соответствовать вашим требованиям):

<?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;
}

Я пробовал пару раз, и, похоже, это работает нормально. Оптимизируйте, если это необходимо. Идея состоит в том, чтобы начать с определения случайной длины для вашего случайного числа, которое поместило бы его в приемлемый диапазон. Затем генерируйте случайные цифры одну за другой до этой длины путем объединения. Если он не находится в диапазоне, сгенерируйте новую случайную цифру в диапазоне и связывать.

Я использую тот факт, что PHP преобразует строку в число, чтобы воспользоваться преимуществами строковых функций. Конечно, это генерирует предупреждение для mt_rand, но поскольку мы используем только числа, его следует безопасно подавить.

Теперь я должен сказать, что мне очень любопытно, зачем вам это нужно в первую очередь.
/* 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;
}
Это всего лишь классическая формула rand_range($min, $max) = $min + rand() % ($max - $min), переведенная в арифметику произвольной точности. Он может проявлять некоторое смещение, если $max - $min не является степенью двойки, но если число битов случайности достаточно велико по сравнению с размером $max - $min, смещение становится незначительным.

Это может сработать:

  • разбейте число на массив с числом 9 или меньше ("остальные")... 9 символов, потому что Макс Рэнд номер 2147483647 на моей машине.
  • для каждого блока массива "9 или менее чисел" создайте случайное число.
  • взорвите массив, и теперь у вас будет полезное случайное число.

Пример кода, иллюстрирующего идею (обратите внимание: код отменен)

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);
}

Испытано и работает

<?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;
}

?>

Генерация ' N ' случайных символов на самом деле не является вариантом, поскольку random('9999999999') все еще теоретически может вернуть 1...

Вот довольно простая функция:

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

Обратите внимание, что он не вернет N бит случайности, просто отрегулируйте масштаб

Возьмите свой этаж и свое случайное число в диапазоне до него.

1225468798745475454898787465154 + rand(0, 6)

Вот псевдокод:


// 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

Примечания:

  • вся арифметика здесь (кроме второй строки) должна быть произвольной точности: используйте библиотеку bcmath для этого
  • во второй строке "длина" - это число цифр, поэтому "длина" 1025 будет равна 4