【问题标题】:Generate unique random strings生成唯一的随机字符串
【发布时间】:2012-11-21 04:16:27
【问题描述】:

我正在用 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 个可能的字符串,但如果数据库收集更多条目,这将需要一些时间。也许它会变成一个几乎无限的递归,我想阻止它。

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

提前致谢

【问题讨论】:

  • 我没有递归的答案,但我确实有一个关于无限猴子最终输入会使修女晕倒的 URL 的提示:some.eg/CuNwha - 你可以拉元音或使用十六进制防止这种情况发生。

标签: perl random dancer


【解决方案1】:

摆脱递归很容易;把你的递归调用变成一个 do-while 循环。例如,将您的功能一分为二; “主要”一个和一个助手。 “主”简单地调用助手并查询数据库以确保它是唯一的。假设 generate_random_string2 是助手,这里是一个骨架:

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

至于在获得有效字符串之前限制迭代次数,仅保存最后生成的字符串并始终将新字符串构建为函数呢?

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

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

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

【讨论】:

  • 这只是将无限递归更改为无限循环。
  • 好的,修改答案以考虑可能的无限循环。
【解决方案2】:

我们真的不需要对你的函数会有多少次迭代(或递归)一无所知。我相信在每次调用时,预期的迭代次数是几何分布的(即第一次成功之前的试验次数由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 次迭代。换句话说,我不会太担心它。

【讨论】:

  • 好的,所以在接下来的 20 年内没有什么可担心的 :D 谢谢
【解决方案3】:

从学术角度来看,这似乎是一个有趣的项目。但是如果你在时钟上并且只需要随机和不同的字符串,我会使用 Data::GUID 模块。

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

my $guid = guid_string();

【讨论】:

  • 是的,这是一个用于完全唯一字符串的好模块。但我需要它们只有六个字符,因为用户必须输入它们......像这样:some.url/Qehy3_ 将重定向到stackoverflow.com
【解决方案4】:

我还有一个基于使用 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();

这会给你一个增量的十六进制值,用非零垃圾填充。

【讨论】:

    猜你喜欢
    • 2010-10-18
    • 1970-01-01
    • 1970-01-01
    • 2011-08-18
    • 2017-02-21
    • 1970-01-01
    • 1970-01-01
    • 2014-09-09
    相关资源
    最近更新 更多