【问题标题】:Generating UNIQUE Random Numbers within a range在一个范围内生成唯一的随机数
【发布时间】:2011-08-02 12:42:33
【问题描述】:

我需要生成一个范围内的随机唯一数字,我该怎么做?我可以通过

生成随机数
generator:
$arr = [];
$x = rand($min, $max);
$len = count($arr);
$flag = 0;
for($i = 0; $i < $len; $i++)
{
 if ($flag === 1)
   goto generator;
 if ($x === $arr[$i])
   $flag = 1;
}
$arr[$index] = $x;
$index++; 
goto generator;

我知道这段代码很糟糕,所以我需要一个更好的优化我的版本的代码! 帮助!

示例: 如果我需要生成 1 到 15 之间的 3 个数字,它们应该是 5、9、1 而不是 3、1、2 [在 1 - 3 中(我想生成的数字)]

【问题讨论】:

  • 唯一随机数? “唯一数”和“随机数”不能共存。想想看。你可以拥有独一无二的。或者你可以有随机的。你要哪个?
  • 我想要随机数并将其存储在数组中,数组中的所有数字必须是唯一的
  • 那些是goto 声明吗?我觉得我正在阅读TI-BASIC...
  • 是的,PHP 仍然支持它:P 对于像我这样的懒人
  • @Soulav 我更新了我的答案,您可能有兴趣阅读。

标签: php


【解决方案1】:

具有随机顺序的数字范围的数组:

$numbers = range(1, 20);
shuffle($numbers);

包装函数:

function UniqueRandomNumbersWithinRange($min, $max, $quantity) {
    $numbers = range($min, $max);
    shuffle($numbers);
    return array_slice($numbers, 0, $quantity);
}

例子:

<?php
print_r( UniqueRandomNumbersWithinRange(0,25,5) );
?>

结果:

 Array
(
    [0] => 14
    [1] => 16
    [2] => 17
    [3] => 20
    [4] => 1
)

【讨论】:

  • 这将包括该范围内的所有个数字,而不是随机数字。
  • 如果只需要 5 个随机数,只需选取数组中的前 5 个项即可。
  • 好点。把它做成一个函数,提交到海量的PHP函数库中;)
  • 嗯? UniqueRandomNumbersWithinRange(1,15,3)完美返回( [0] =&gt; 13 [1] =&gt; 20 [2] =&gt; 15 )
  • 如果您想要高随机数,这不是一个好选择。想想这个函数需要多长时间才能生成 1 到 50k 之间的 30 个兰特数。它将首先创建一个包含 50000 个整数的数组,然后尝试对它们中的每一个进行打乱,然后从另一个 50000 开始切片。
【解决方案2】:
$len = 10;   // total number of numbers
$min = 100;  // minimum
$max = 999;  // maximum
$range = []; // initialize array
foreach (range(0, $len - 1) as $i) {
    while(in_array($num = mt_rand($min, $max), $range));
    $range[] = $num;
}
print_r($range);

我很想看看接受的答案如何与我的答案相提并论。值得注意的是,两者的混合可能是有利的;实际上是一个根据某些值有条件地使用一个或另一个的函数:

# The accepted answer
function randRange1($min, $max, $count)
{
    $numbers = range($min, $max);
    shuffle($numbers);
    return array_slice($numbers, 0, $count);
}

# My answer
function randRange2($min, $max, $count)
{
    $range = array();
    while ($i++ < $count) {
        while(in_array($num = mt_rand($min, $max), $range));
        $range[] = $num;
    }
    return $range;
}

echo 'randRange1: small range, high count' . PHP_EOL;
$time = microtime(true);
randRange1(0, 9999, 5000);
echo (microtime(true) - $time) . PHP_EOL . PHP_EOL;

echo 'randRange2: small range, high count' . PHP_EOL;
$time = microtime(true);
randRange2(0, 9999, 5000);
echo (microtime(true) - $time) . PHP_EOL . PHP_EOL;

echo 'randRange1: high range, small count' . PHP_EOL;
$time = microtime(true);
randRange1(0, 999999, 6);
echo (microtime(true) - $time) . PHP_EOL . PHP_EOL;

echo 'randRange2: high range, small count' . PHP_EOL;
$time = microtime(true);
randRange2(0, 999999, 6);
echo (microtime(true) - $time) . PHP_EOL . PHP_EOL;

结果:

randRange1: small range, high count
0.019910097122192

randRange2: small range, high count
1.5043621063232

randRange1: high range, small count
2.4722430706024

randRange2: high range, small count
0.0001051425933837

如果您使用较小的范围和较高的返回值计数,那么接受的答案肯定是最佳的;但是,正如我所料,更大的范围和更小的计数将花费更长的时间来接受答案,因为它必须将每个可能的值存储在范围内。你甚至冒着破坏 PHP 内存上限的风险。评估范围和计数之间的比率并有条件地选择生成器的混合将是两全其美的。

【讨论】:

  • 正是我想要的:)
  • 我非常感谢您的代码,它的执行速度比选定的 ans 快得多,但在 [1-1000] 范围内失败!
  • 您打错了:最后一个结果的名称不是randRange1,而是randRange2
  • 谢谢@JasomDotnet :-)
  • 如果是这样,我们可以结合两个答案,并根据范围和计数做出一个 if 条件。如果有人可以检查范围和计数阈值的最佳值,那将是完美的
【解决方案3】:

这个想法包括使用键,当数组键中已经存在一个值时,数组大小保持不变:

function getDistinctRandomNumbers ($nb, $min, $max) {
    if ($max - $min + 1 < $nb)
        return false; // or throw an exception

    $res = array();
    do {
        $res[mt_rand($min, $max)] = 1;
    } while (count($res) !== $nb);
    return array_keys($res); 
}

Pro:这种方式避免了使用in_array 并且不会生成一个巨大的数组。因此,它速度快,并且可以节省大量内存。

缺点:当速率(范围/数量)降低时,速度也会降低(但保持正确)。对于相同的速率,相对速度随着范围大小而增加。(*)

(*) 我理解这一事实,因为有更多的免费整数可供选择(特别是对于第一步),但如果有人有描述这种行为的数学公式,我感兴趣,不要犹豫。

结论:最好的“通用”函数似乎是这个函数和@Anne 函数的混合,它的效率更高,而且效率更高。当需要一定数量并达到速率(范围/数量)时,此功能应在两种方式之间切换。因此,必须考虑到测试的复杂性/时间。

【讨论】:

  • @Bell:谢谢你的指正,我确实错过了。
  • 这个需要点赞!当我按投票排序线程时,我发布了类似的内容。 !==$nb 是一个很好的性能技巧。您可能想在第一行添加$nb=(int)$nb 并将$min$max 也转换为int,以确保。
  • CMIIW,但我认为它可以生成与之前设置的相同的密钥,因此如果$nb 的值非常大并且与$max - $min 接近,它是不可靠的并且可能导致运行时间过长
  • @HendraWD:你错了,但以一种特殊的方式:例如,当你想要 0 到 1.000.000 之间的 999.999 个随机值时,没有理由使用这个函数(在 a 中删除 2 个数字更容易)洗牌范围)。无论如何,对于您所谈论的价值观,我认为这里的所有解决方案都不是因为一个基本原因而真正合适的:谁想要以一百万个或更多项目的数组结束?在这些情况下,使用生成器函数和“惰性求值”策略是更合适的方法。
  • @HendraWD:请注意,我已经在答案的“缺点”部分描述了这种现象,并提出了解决方法。
【解决方案4】:

如果你想生成 100 个随机数字,但每个数字只出现一次,一个好方法是生成一个按顺序排列的数字数组,然后将其打乱。

类似这样的:

$arr = array();

for ($i=1;$i<=101;$i++) {
    $arr[] = $i;
}

shuffle($arr);

print_r($arr);

输出将如下所示:

Array
(
    [0] => 16
    [1] => 93
    [2] => 46
    [3] => 55
    [4] => 18
    [5] => 63
    [6] => 19
    [7] => 91
    [8] => 99
    [9] => 14
    [10] => 45
    [11] => 68
    [12] => 61
    [13] => 86
    [14] => 64
    [15] => 17
    [16] => 27
    [17] => 35
    [18] => 87
    [19] => 10
    [20] => 95
    [21] => 43
    [22] => 51
    [23] => 92
    [24] => 22
    [25] => 58
    [26] => 71
    [27] => 13
    [28] => 66
    [29] => 53
    [30] => 49
    [31] => 78
    [32] => 69
    [33] => 1
    [34] => 42
    [35] => 47
    [36] => 26
    [37] => 76
    [38] => 70
    [39] => 100
    [40] => 57
    [41] => 2
    [42] => 23
    [43] => 15
    [44] => 96
    [45] => 48
    [46] => 29
    [47] => 81
    [48] => 4
    [49] => 33
    [50] => 79
    [51] => 84
    [52] => 80
    [53] => 101
    [54] => 88
    [55] => 90
    [56] => 56
    [57] => 62
    [58] => 65
    [59] => 38
    [60] => 67
    [61] => 74
    [62] => 37
    [63] => 60
    [64] => 21
    [65] => 89
    [66] => 3
    [67] => 32
    [68] => 25
    [69] => 52
    [70] => 50
    [71] => 20
    [72] => 12
    [73] => 7
    [74] => 54
    [75] => 36
    [76] => 28
    [77] => 97
    [78] => 94
    [79] => 41
    [80] => 72
    [81] => 40
    [82] => 83
    [83] => 30
    [84] => 34
    [85] => 39
    [86] => 6
    [87] => 98
    [88] => 8
    [89] => 24
    [90] => 5
    [91] => 11
    [92] => 73
    [93] => 44
    [94] => 85
    [95] => 82
    [96] => 75
    [97] => 31
    [98] => 77
    [99] => 9
    [100] => 59
)

【讨论】:

  • thnx 为您的代码,但生成的数字取决于大小:(如果我尝试生成 3 个数字,它们就像 1、3、2 我想要像 1、5、15 [1-15限制]非常感谢您的帮助:|
  • 啊,你的问题不是特别清楚 - 可能值得重写它以使其更清楚!
【解决方案5】:

如果你需要 5 个介于 1 和 15 之间的随机数,你应该这样做:

var_dump(getRandomNumbers(1, 15, 5));

function getRandomNumbers($min, $max, $count)
{
    if ($count > (($max - $min)+1))
    {
        return false;
    }
    $values = range($min, $max);
    shuffle($values);
    return array_slice($values,0, $count);
}

如果您指定的计数值大于可能的数字范围,它将返回 false。

【讨论】:

  • 注意,这是如果您不想显示范围内的所有数字。如果您这样做,请使用其他人提供的随机答案。
  • thnx 为您的代码,但生成的数字取决于大小:( 已弃用:调用时传递引用已被弃用,它使用大量内存!
  • var_dump(getRandomNumbers(1, 15, 5)); function getRandomNumbers($min, $max, $count) 我认为有问题,我尝试使用这些参数 (100,999,10);
  • 试试这个 var_dump(getRandomNumbers(1, 5, 4));
  • 是的,它运行良好 :) 但无法选择作为您的答案,因为您的函数耗时 9.9999999999989E-5 秒 vs 5.99999999999949E-5 秒,上帝保佑
【解决方案6】:

你可以试试下一个代码:

function unique_randoms($min, $max, $count) {

 $arr = array();
 while(count($arr) < $count){
      $tmp =mt_rand($min,$max);
      if(!in_array($tmp, $arr)){
         $arr[] = $tmp;
      }
 }
return $arr;
}

【讨论】:

    【解决方案7】:

    获取一个随机数。它是否已经存储在数组中?如果没有,请存储它。如果是这样,那就去获取另一个随机数并重复。

    【讨论】:

      【解决方案8】:

      在创建需要在更大范围内生成 30,000 个唯一数字的应用程序时,我能够使用这种方法将处理时间从 25 秒缩短到 1.5 秒。

      这个想法是 PHP 在生成随机数方面比在检查数组中是否存在项时要快得多 - 这就是为什么使用 while(in_array) 循环可能会很慢。

      $count = 0;
      $collectNumbers = [];
      while ($count < 30000) {
      for ($i = 0; $i < 60000; $i++) {
      
      $rand = mt_rand(1, 100000);
      $collectNumbers[] = $rand;
      
      }
      $unique = array_unique($collectNumbers);
      $count = count($unique);
      }
      
      $finalArray = array_slice($unique, 0, 30000);
      

      这将非常快速地返回 30,000 个唯一数字。使用两倍于您需要生成的数字量的迭代值可以增加第一次迭代中唯一数字的可能性......但无论需要多少次迭代,这都会比检查数组中的重复数字更快地产生结果在一个循环中。

      【讨论】:

        【解决方案9】:

        我想这对大多数人来说可能不是问题,但我试图解决它。我想我有一个相当不错的解决方案。以防其他人偶然发现此问题。

        function randomNums($gen, $trim, $low, $high)
        {
            $results_to_gen = $gen;
            $low_range      = $low;
            $high_range     = $high;
            $trim_results_to= $trim;
        
            $items = array();
            $results = range( 1, $results_to_gen);
            $i = 1;
        
            foreach($results as $result)
            {
                $result = mt_rand( $low_range, $high_range);
                $items[] = $result;
        
            }
        
        
            $unique = array_unique( $items, SORT_NUMERIC);
            $countem = count( $unique);
            $unique_counted = $countem -$trim_results_to;
        
            $sum = array_slice($unique, $unique_counted);
        
        
            foreach ($sum as $key)
            {
                $output = $i++.' : '.$key.'<br>';
                echo $output;
            }
        
        }
        

        randomNums(1100, 1000 ,890000, 899999);

        【讨论】:

          【解决方案10】:

          这可能会解决你的问题:

          <?php print_r(array_rand(range(1,50), 5)); ?>
          

          【讨论】:

            【解决方案11】:

            这就是我的做法。

            $randnum1 = mt_rand(1,20);
            
            $nomatch = 0;
            
            while($nomatch == 0){
            
            $randnum2 = mt_rand(1,20);
            
            if($randnum2 != $randnum1){
            
            $nomatch = 1;
            
            }
            
            }
            
            $nomatch = 0;
            
            while($nomatch == 0){
            
            $randnum3 = mt_rand(1,20);
            
            if(($randnum3 != $randnum1)and($randnum3 != $randnum2)){
            
            $nomatch = 1;
            
            }
            
            }
            

            然后你可以回显结果来检查

            echo "Random numbers are " . $randnum1 . "," . $randnum2 . ", and " . $randnum3 . "\n";
            

            【讨论】:

              【解决方案12】:

              “shuffle”方法有一个重大错误。当数字很大时,shuffle 30 亿个索引会立即导致 500 错误。这是处理非常大的数字的最佳解决方案。

              function getRandomNumbers($min, $max, $total) {
                  $temp_arr = array();
                  while(sizeof($temp_arr) < $total) $temp_arr[rand($min, $max)] = true;
                  return $temp_arr;
              }
              

              假设我想获得 10 个从 10 亿到 40 亿的唯一随机数。

              $random_numbers = getRandomNumbers(1000000000,4000000000,10);
              

              PS:执行时间:0.027微秒

              【讨论】:

                【解决方案13】:

                只需使用此函数并传递您要生成的数字的计数

                代码:

                function randomFix($length)
                {
                    $random= "";
                
                srand((double)microtime()*1000000);
                
                $data = "AbcDE123IJKLMN67QRSTUVWXYZ";
                $data .= "aBCdefghijklmn123opq45rs67tuv89wxyz";
                $data .= "0FGH45OP89";
                
                for($i = 0; $i < $length; $i++)
                {
                    $random .= substr($data, (rand()%(strlen($data))), 1);
                }
                return $random;}
                

                【讨论】:

                  【解决方案14】:

                  生成唯一随机数的最佳方法是

                  <?php
                       echo md5(uniqid(mt_rand(), true).microtime(true));
                  ?>
                  

                  【讨论】:

                    猜你喜欢
                    • 2014-05-15
                    • 2011-05-16
                    • 2018-04-08
                    • 1970-01-01
                    • 1970-01-01
                    • 1970-01-01
                    • 2018-12-17
                    • 2015-12-22
                    • 1970-01-01
                    相关资源
                    最近更新 更多