随机生成器返回无限重复

时间:2016-03-04 12:18:08

标签: php

我正在尝试创建一个随机字符串,用作短引用号。我花了最近几天试图让它工作,但它似乎达到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服务器并且没有重复。

在我们的环境中可能会导致这种行为?

1 个答案:

答案 0 :(得分:0)

代码对我来说过于复杂。让我们假设你真的想要创建 n 唯一的字符串,每个字符串都基于一个随机值(rand / mt_rand / INT_MIN,INT_MAX之间的东西)。

您可以从编码中解耦随机值的生成开始(代码中似乎没有任何东西使字符串依赖于任何先前的状态 - 对唯一性的感知)。比较整数比比较任意字符串要快得多 mt_rand()返回INT_MIN和INT_MAX之间的任何内容,使用32位整数(也可能是64位,取决于如何编译php),它提供~2 32 元素。你想选择200k,让它成为400k,这是价值范围的1/10000。因此,假设一切都与唯一性相符合理是合理的......然后再检查一下。如果发生碰撞,请添加更多值。再次比在循环的每次迭代中检查in_array要快得多 一旦有足够的值,就可以将它们编码/转换为您想要的格式。我不知道<digit><character>-<something>格式是强制性的,但假设它不是 - &gt; 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];

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

使用base_convert(),可以成为li10之类的字符串,如果你必须手动输入字符串,解密就会非常烦人。