生成唯一的随机字符串

时间:2012-12-03 16:34:44

标签: perl random dancer

我正在与Dancer一起写一个非常小的URL缩短器。它使用REST插件将发布的URL存储在数据库中,该数据库具有六个字符的字符串,用户可以使用该字符串访问短路的URL。

现在我对我的随机字符串生成方法有点不确定。

sub generate_random_string{
    my $length_of_randomstring = shift; # the length of 
                                        # the random string to generate

    my @chars=('a'..'z','A'..'Z','0'..'9','_');
    my $random_string;
    for(1..$length_of_randomstring){
        # rand @chars will generate a random 
        # number between 0 and scalar @chars
        $random_string.=$chars[rand @chars];
    }

    # Start over if the string is already in the Database
    generate_random_string(6) if database->quick_select('urls', { shortcut => $random_string });

    return $random_string;
}

这会生成一个六字符串,如果生成的字符串已经在数据库中,则会递归调用该函数。我知道有63 ^ 6个可能的字符串,但如果数据库收集更多条目,这将需要一些时间。也许它会变成一个几乎无限的递归,我想阻止它。

是否有办法生成唯一的随机字符串,以防止递归?

提前致谢

4 个答案:

答案 0 :(得分:5)

我们真的不需要手动波动你的函数有多少次迭代(或递归)。我相信在每次调用时,预期的迭代次数是按地理分布的(即第一次成功之前的试验次数由geomtric distribution控制),其平均值为1 / p,其中p是成功找到未使用的概率串。我相信p只是1 - n / 63 ^ 6,其中n是当前存储的字符串的数量。因此,我认为你需要在数据库中存储300亿个字符串(~63 ^ 6/2),然后你的函数平均每次调用超过2次(p = .5)。

此外,几何分布的方差为1-p / p ^ 2,因此即使在300亿个条目中,一个标准差也只是sqrt(2)。因此,我预计〜99%的时间循环将花费2 + 2 * sqrt(2)交互或~5次迭代。换句话说,我不会太担心它。

答案 1 :(得分:4)

从学术立场来看,这似乎是一个有趣的计划。但是,如果你在时钟上,只需要随机和不同的字符串,我将使用Data :: GUID模块。

use strict;
use warnings;
use Data::GUID qw( guid_string );

my $guid = guid_string();

答案 2 :(得分:2)

摆脱递归很容易;将递归调用转换为do-while循环。例如,将您的功能分成两部分; “主要”和帮助者。 “main”只调用帮助程序并查询数据库以确保它是唯一的。假设generate_random_string2是帮助器,这是一个骨架:

do {
   $string = generate_random_string2(6);
} while (database->quick_select(...));

至于在获取有效字符串之前限制迭代次数,那么只保存最后生成的字符串并始终构建新字符串作为其函数呢?

例如,当你开始时,你没有字符串,所以我们只说你的字符串是'a'。然后,下次构建字符串时,将获得最后构建的字符串('a')并对其应用转换,例如递增最后一个字符。这给你'b'。等等。最终你达到了你所关心的最高性格(比如'z'),此时你附加'a'来获得'za',然后重复。

现在没有数据库,只有一个持久值可用于生成下一个值。当然,如果你想要真正的随机字符串,你必须使算法更复杂,但基本原理是相同的:

  1. 您当前的值是上次存储值的函数。
  2. 生成新值时,请将其存储。
  3. 确保您的世代将产生一个独特的价值(之前没有发生过的价值)。

答案 3 :(得分:1)

基于使用MySQL,我还有一个想法。

create table string (
    string_id int(10) not null auto_increment,
    string varchar(6) not null default '',
    primary key(string_id)
);

insert into string set string='';

update string
    set string = lpad( hex( last_insert_id() ), 6, uuid() )
    where string_id = last_insert_id();

select string from string
    where string_id = last_insert_id();

这为您提供了一个增量十六进制值,该值用非零垃圾填充。