PHP - 改进随机数序列生成

时间:2015-08-05 07:24:13

标签: php random numbers

我想这是关于SO的第一个问题。

我目前正在一个网站上工作,我必须在1到29之间生成6个数字(每个最大一个)用于抽奖。因为它们可以按任何顺序排列,我只是在事后对它们进行排序。

如果我没弄错的话,那应该意味着(29*28*27*26*25*24) / 6! = 475020种不同的组合。

我尝试过使用mt_rand或random_int(来自random_compat)生成序列的不同方法,但是当我用10k次迭代测试它时,我总是得到大约100个重复,即使它们像465k组合仍然可用。

以下是我一直在尝试的代码示例:

// Using an array and mt_rand (or random_int, giving same results)
// Also tried shuffling the array instead of simply reindexing it, not better

$values = range(1, 29);

while(count($values) > 6) {
    unset($values[mt_rand(0, count($values) - 1)]);
    $values = array_values($values);
}



// Creating the array from random numbers (same results using random_int)

$values = array();
while (count($values) < 6) {
    $r = mt_rand(1, 29);
    if (in_array($r, $values)) {
        continue;
    } else {
        $values[] = $r;
    }
}

那么好......我的问题是:

  • 有没有办法改善我目前正在做的事情?
  • 这是它的方式,我必须处理它?<​​/ li>
  • 我只是做错了吗?

谢谢!

林恩。

PS:看了很多问题,但没有找到满足我需求的任何内容,抱歉,如果我看起来不够好的话!

只是为了清楚地说明一些事情:使用random_int(使用/ dev / urandom或openssl_random_pseudo_bytes)并没有改进任何东西,我认为这样做。如果可能的话,我不想使用任何外部API(如random.org)。

5 个答案:

答案 0 :(得分:1)

了解Birthday Paradox

根据我的计算(bc calculator),获得29个项目中的6个的重复组合的概率为50%或更好,812个序列数。

define p(n, k) { return (n-k)/n; }
n=475020
m=1; for (k=0; k<811; k++) m *= p(n, k); m
.500649663424
m=1; for (k=0; k<812; k++) m *= p(n, k); m
.499794905988

答案 1 :(得分:1)

  

使用random_int(使用/ dev / urandom或openssl_random_pseudo_bytes)并没有改进任何东西,我认为这样做。

当然可以,它不是你可以直观识别的东西。 mt_rand()rand()仅允许约2 32 可能的种子和2 32 可能的输出,最重要的是,具有确定性序列if you know a few outputs, you can predict the rest until it's reseeded

您的操作系统的CSPRNG没有任何此类限制。知道少数random_int()输出(在PHP中限制为32位系统上的2 32 可能值,64位系统上2 64 )没有给你任何关于未来产出的信息。

  

我目前正在一个网站上工作,我必须在1到29之间生成6个数字(每个最大一个)用于抽奖。因为它们可以按任何顺序排列,我只是在事后对它们进行排序。

好的,这是一个很好的主意。你肯定想要一个CSPRNG。

  

当我用10k迭代测试它时,我总是得到大约100个重复,即使它们仍然像465k组合仍然可用。

正如其他人所指出的,这是birthday problem/paradox正在发挥作用。

如果您需要解决方案,请尝试以下方法:

function random_unique_select($num, array $possible_values)
{
    $sizeof = count($possible_values);
    if ($num > $sizeof) {
        throw new InvalidArgumentException('$num is too large');
    }
    $selected = [];
    for ($i = 0; $i < $num; ++$i) {
        // Grab a random int [0, ... N - 1]
        $r = random_int(0, $sizeof - 1);
        // Copy the selected value into $selected
        $selected[] = $possible_values[$r];
        // Delete it from the range of possible values
        unset($possible_values[$r]);
        // N has grown smaller by 1
        --$sizeof;
        // Reset keys; we want this to be zero-indexed.
        $possible_values = array_values($possible_values);
    }
    return $selected;
}

$lottery = random_unique_select(6, range(1,29));

演示:

答案 2 :(得分:0)

要改善“随机性”,您可以尝试使用加密库,例如phpseclib

他们的数学库here中有一个random()函数。

编辑:计算机生成的数字不能随机。使用加密库可以得到最好的伪随机结果,最简单,最随机的解决方案是@Matthias Leuffen。

答案 3 :(得分:0)

rand()和mt_rand()依靠纯数学来产生伪随机数。

要获得真实的随机数,您可以使用http://www.random.org

的网络服务

答案 4 :(得分:-1)

如果安装了正确的扩展程序,您可以使用openssl_random_pseudo_bytes()

示例:

function strong_random() {
    return hexdec(bin2hex(openssl_random_pseudo_bytes(20)));
}

注意:由于openssl_random_pseudo_bytes()的实施,此功能将非常慢

当然,快速和肮脏可以使用添加最大长度参数。