随机生成器返回无穷无尽的重复项


Random generator returning endless duplicates

我正在尝试创建一个随机字符串,该字符串将用作简短的参考编号。在过去的几天里,我一直在试图让它工作,但它似乎达到了大约 32766 条记录,然后它继续无休止地重复。我至少需要 200,000 个变体。

下面的代码是一个非常简单的模型来解释发生了什么。代码应该根据 1a-x1y2z(示例)进行语法,这应该比 32k 给出更多的结果

我有一种感觉,这可能与记忆有关,但不确定。有什么想法吗?

<?php
function createReference() {
    $num = rand(1, 9);
    $alpha = substr(str_shuffle("abcdefghijklmnopqrstuvwxyz"), 0, 1);
    $char = '0123456789abcdefghijklmnopqrstuvwxyz';
    $charLength = strlen($char);
    $rand = '';
    for ($i = 0; $i < 6; $i++) {
        $rand .= $char[rand(0, $charLength - 1)];
    }
    return $num . $alpha . "-" . $rand;
}
$codes = [];
for ($i = 1; $i <= 200000; $i++) {
    $code = createReference();
    while (in_array($code, $codes) == true) {
        echo 'Duplicate: ' . $code . '<br />';
        $code = createReference();
    }
    $codes[] = $code;
    echo $i . ": " . $code . "<br />";
}
exit;
?>

更新

所以我开始怀疑这是否与我们的 WAMP 设置 (Bitnami) 无关,因为我们的本地机器在开始复制之前正好达到 1024 条记录。通过从上面的字符串中删除 1 个字符(而不是 for 循环中的 6 个字符,我将其设为 5),它正好得到 32768 条记录。

我把脚本上传到我们的 centos 服务器,没有重复的脚本。

在我们的环境中,什么会导致这种行为?

代码对我来说看起来过于复杂。让我们假设您现在真的想创建 n 个唯一字符串,每个字符串基于单个随机值(rand/mt_rand/介于 INT_MIN,INT_MAX 之间的值)。

您可以从将随机值的生成与编码分离开始(代码中似乎没有任何内容使字符串依赖于任何先前的状态 - 唯一性除外)。比较整数比比较任意字符串要快得多。
mt_rand() 返回 INT_MIN 到 INT_MAX 之间的任何内容,使用 32 位整数(也可以是 64 位,取决于 PHP 的编译方式),给出 ~232 个元素。你想选择 200k,让我们把它变成 400k,即 ~ 值范围的 1/10000。因此,假设一切都与独特性相得益彰是合理的......然后稍后检查。并在发生冲突时添加更多值。再次比在循环的每次迭代中检查in_array要快得多。
获得足够的值后,可以将它们编码/转换为所需的格式。我不知道<digit><character>-<something>格式是否是强制性的,但假设它不是 -> base_convert()

<?php
function unqiueRandomValues($n) {
    $values = array();
    while( count($values) < $n ) {
        for($i=count($values);$i<$n; $i++) {
            $values[] = mt_rand();
        }
        $values = array_unique($values);
    }
    return $values;
}
function createReferences($n) {
    return array_map(
        function($e) {
            return base_convert($e, 10, 36);
        },
        unqiueRandomValues($n)
    );
}
$start = microtime(true);
$references = createReferences(400000);
$end = microtime(true);
echo count($references), ' ', count(array_unique($references)), ' ', $end-$start, ' ', $references[0];

印刷品,例如 400000 400000 3.3981630802155 f3plox我的 i7-4770 上。($end-$start部分始终在 3.2 和 3.4 之间)

使用 base_convert() 可以有像 li10 这样的字符串,如果您必须手动键入字符串,破译起来可能会很烦人。