选择长度最多为n的均匀随机字符串?

时间:2013-04-07 23:07:52

标签: string algorithm random

假设我想从所有长度最多为n的字符串中选择一个均匀随机的字符串(假设有一个固定的字符集可以组成字符串,例如字母A-Z)。如果我事先知道字符串的长度是什么,我可以通过随机选择该字符串的每个字符来轻松选择随机字符串。但是,为了保证我们随机均匀地选择字符串,我不能只选择一个随机的均匀长度,然后选择一个长度的随机字符串,因为如果你要选择一个完全随机的字符串,那么它往往不会有长度大于较短长度,因为长串比短串长。

是否有一种已知的算法可以随机均匀地选择最多n个长度的字符串?

谢谢!

6 个答案:

答案 0 :(得分:2)

每个字母都会添加另一个可能字符数的因子,因此有26个单字母字符串,26×26个双字母字符串,依此类推。您只需要通过相应的缩放来随机选择一个长度。

E.g。您可以选择最多308915776的随机数并选择字符串长度,如下所示:

< 26        - 1
< 702       - 2
< 15576     - 3
< 456976    - 4
< 11881376  - 5
< 308915776 - 6

这些数字很快就会变得有点大,所以只要你的 n 很小,它就可以运作。否则,您可以使用浮点数并使用0到1之间的范围。

答案 1 :(得分:2)

有26个字符,长度最多为n。所以字符串的总数是:

Total Number of Strings = \sum_{i=1}^n 26^i

我们需要以相同的概率选择这些中的每一个:

P(string s is chosen) = 1 / TotalNumStrings

现在考虑您提出的选择随机长度然后选择该长度的随机字符串的策略。所以按照贝叶斯规则,我们有:

P(string s being chosen which has length i) =
     P(string s being chosen | string has length i) *
     P(we want a string of length i) = (1 / 26^i) * (1 / n) = 1 / (26^i * n)

不等于1 / TotalNumStrings。你已经知道这不起作用,但这会激发正确的选择策略。

现在选择字符串如下:

P(string s being chosen which has length i) =
     P(string s being chosen | string has length i) *
     P(we want a string of length i) = 
         1 / (26^i) *  P(chosen string has length i) = 1 / NumStrings.

因此我们有P(选择的字符串有长度i)= 26 ^ i / NumStrings!多田。

所以总结一下选择策略如下。首先选择长度为i,概率为26 ^ i / NumStrings。然后在该类别中选择一个任意字符串。

答案 2 :(得分:2)

n的分布减去均匀随机串的长度与X mod(n + 1)相同,其中X是具有范围[0,无穷大]的几何,并且成功概率1-1 / k并且k是字母表中的字母数。要随机选择一个完全均匀的字符串并且不使用bignums:对几何mod(n + 1)进行采样(例如,通过均匀地采样字母,直到不是A的字母出现,然后返回非As生成的mod的数量(的n + 1))。生成一个长度为n的字符串减去该值。

答案 3 :(得分:0)

考虑到字符集的大小,你不能只计算出长度的分布吗?

确定长度k字符串与长度短于k的字符串的比率。这来自:wikipedia

因此,假设一个最大字符串,然后随机确定一个较短字符串的相对概率。

如果更短,请重复以查看n-1或更少。

我认为这种方法可以合理地干净地处理舍入错误。 n合理大小时实际获得非常短的字符串的可能性仍然非常小,但具有代表性。

要做总结,我们想要:

k^n samples of length n
k^(n-1) of length n-1
etc.
k of length 1
1 of length 0

p(length < x)/p(length <= x)
= sum(1+..+k^x-1)/sum(1+..+k^x)
= (1 - k^-x)/ (k-k^-x)

所以我们可以这样实现:

int getLength(int n, int setSize)
{
    if (n == 0)
        return 0;
    double oneOverKtoTheN = pow(1.0/setSize, (double)n);
    double pLengthN = (1-oneOverKtoTheN)/(setSize - oneOverKtoTheN);
    double sample = ((double) rand()) / RAND_MAX;
    if (sample < pLengthN)
        return n;
    return getLength(n-1, setSize);
}

请注意,由于浮动点开始,oneOverKtoTheN可能会被删除,但随着n的减少,它开始按计划进行计数。

答案 4 :(得分:0)

如果需要更大的字符串,另一种方法是从27个字符中随机构建每个字符串,其中第27个字符串是字符串结尾字符。您将不得不拒绝任何超过最大可接受n的字符串,但生成应该相当有效。因此,要生成具有“正确”分布和长度在0到n范围内的随机字符串,您可以使用以下更高效的版本:

Function RandomString(n : integer) : string;
var
  RandomChar : char;
begin
  result := '';
  repeat
    RandomChar := Char('a' + Random(27));
    if RandomChar in ['a'..'z'] then
      result := result + RandomChar;
    if Length(result) > n then
      result := '';
  until RandomChar not in ['a'..'z'];
end;

答案 5 :(得分:0)

长度为n的所有字符串的数量为(26^(n+1)-1)/(26-1)

想法是决定字符串是否为空。这可能性是(26-1)/(26^(n+1)-1)。为了生成这种概率的事件,我们生成26^(n+1)个事件,忽略其中一个事件并从其余事件中选择25个事件。

char GenerateRandomCharacter()
{
    ...
}
std::string GenerateRandomStringOfFixedLength(int length)
{
    std::string result;
    for(int c=0;c<length;++c)
        result.push_back(c);
    return result;
}
bool WillWeGenerateEmptyString(int maxLength)
{
    while(true)
    {
        const std::string sample=GenerateRandomStringOfFixedLength(maxLength+1);
        if(sample==std::string(maxLength+1,'A'))
            continue;//this leaves 26^n-1 values
        else
            return sample.substr(1)==std::string(maxLength,'A');//only 25 strings satisfy this
    }
}
std::string Generate(int maxLength)
{
    if(WillWeGenerateEmptyString(maxLength))
        return std::string();
    else
    {
        std::string result;
        result.push_back(GenerateRandomCharacter());
        result+=Generate(maxLength-1);
        return result;
    }
}