关于php:生成范围内的唯一随机数


Generating UNIQUE Random Numbers within a range

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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
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(我想生成的数字)]


以随机顺序排列数字范围的数组:

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

包装功能:

1
2
3
4
5
function UniqueRandomNumbersWithinRange($min, $max, $quantity) {
    $numbers = range($min, $max);
    shuffle($numbers);
    return array_slice($numbers, 0, $quantity);
}

例:

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

结果:

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


1
2
3
4
5
6
7
8
9
$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);

我很想知道被接受的答案如何与我的相对。有必要指出的是,两者的混合可能是有利的。实际上,一个函数根据某些值有条件地使用一个或另一个:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
# 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;

结果:

1
2
3
4
5
6
7
8
9
10
11
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内存上限的风险。评估范围和计数之间的比率并有条件地选择生成器的混合动力是两全其美的。


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

1
2
3
4
5
6
7
8
9
10
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);
}

优点:这样可以避免使用in_array并且不会生成巨大的数组。因此,它速度很快,并保留了大量内存。

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

(*)我了解这一事实,因为有更多的自由整数可供选择(尤其是对于第一步),但是如果有人具有描述此行为的数学公式,我很感兴趣,请不要犹豫。

结论:最好的"常规"函数似乎是此函数与@Anne函数之间的混合,后者效率更高,而且效率极低。当需要一定数量并且达到速率(范围/数量)时,此功能应在两种方式之间切换。因此,必须考虑到要知道的测试的复杂性/时间。


您可以尝试下一个代码:

1
2
3
4
5
6
7
8
9
10
11
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;
}

如果要生成100个随机数字,但每个数字仅出现一次,则一种好方法是按顺序生成一个具有数字的数组,然后将其随机播放。

像这样:

1
2
3
4
5
6
7
8
9
$arr = array();

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

shuffle($arr);

print_r($arr);

输出将如下所示:

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


如果需要1至15之间的5个随机数,则应执行以下操作:

1
2
3
4
5
6
7
8
9
10
11
12
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。


获取一个随机数。它已经存储在数组中了吗?如果没有,请将其存储。如果是这样,则去获取另一个随机数并重复。


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

这个想法是PHP生成随机数要比检查数组中项目的存在快得多,这就是为什么使用while(in_array)循环会很慢的原因。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
$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个唯一数字。 使用两倍于您需要生成的数字数量的迭代值可以增加唯一数字在第一次迭代中的可能性...但是,不管它需要进行多少次迭代,其生成结果的速度都比检查数组中重复数字的速度更快 在一个循环中。


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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
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.'';
        echo $output;
    }

}

randomNums(1100,1000,890000,899999);


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

码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
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;}

"随机播放"方法具有主要错误。当数字很大时,洗掉30亿个索引将立即导致500错误。这是针对大量用户的最佳解决方案。

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

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

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

PS:执行时间:0.027微秒


这就是我要做的。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
$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;

}

}

然后您可以回显结果以进行检查

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

这可能会解决您的问题:

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

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

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