【问题标题】:fast generate a large set of random strings from number and alphabet快速从数字和字母生成大量随机字符串
【发布时间】:2013-10-22 10:40:00
【问题描述】:

我必须生成一个 字符串集(10k 甚至更多),大小为 32 个字符,随机从“a-z”、“A-Z”和“0-9” .

到目前为止,我脑子里有以下代码 (O(N*32)),但我想知道是否有更好的方法来做到这一点。

int N = 10000;           
vector<string> vecStr;

for (int index=0; index<N; index++)
{
  string str;
  for (int i = 0; i < 32; ++i)
  {
    int randomChar = rand()%(26+26+10);        
    if (randomChar < 26)
      str += 'a' + randomChar;
    else if (randomChar < 26+26)
      str += 'A' + randomChar - 26;
    else
      str += '0' + randomChar - 26 - 26;
  }
  vecStr.push_back(str);
} 

【问题讨论】:

标签: c++ algorithm random shuffle


【解决方案1】:

你不会找到比 O(N*len) 更好的解决方案,其中 N 是字符串的数量,len 是其中每个字符串的长度。也就是说,在某个地方,我确信我可以通过编写 最密集 代码来实现这一点:

#include <iostream>
#include <iterator>
#include <vector>
#include <random>
#include <algorithm>

int main()
{
    static const char alphabet[] =
        "abcdefghijklmnopqrstuvwxyz"
        "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
        "0123456789";

    static const size_t N_STRS = 10000;
    static const size_t S_LEN = 32;

    std::random_device rd;
    std::default_random_engine rng(rd());
    std::uniform_int_distribution<> dist(0,sizeof(alphabet)/sizeof(*alphabet)-2);

    std::vector<std::string> strs;
    strs.reserve(N_STRS);
    std::generate_n(std::back_inserter(strs), strs.capacity(),
        [&] { std::string str; 
              str.reserve(S_LEN); 
              std::generate_n(std::back_inserter(str), S_LEN,
                   [&]() { return alphabet[dist(rng)];}); 
              return str; });
    std::copy(strs.begin(), strs.end(), std::ostream_iterator<std::string>(std::cout, "\n"));
    return 0;
}

输出(为简洁省略9990行=P)

MRdeOWckfKy8GTFt0YmQMcM6SABJc934
XvdcatVsv6N9c1PzQGFFY6ZP943yIrUY
xpHzxUUyAizB6BfKldQzoePrm82PF1bn
kMUyPbflxk3yj3IToTFqYWnDq6aznKas
Ey0W5SF37VaeEY6PxWsBoxlNZTv9lOUn
iTx7jFRTHHW6TfYl7N3Hne4yu7kgAzp5
0ZamlaopjLyEvJbr6fzJPdXmjLOohtKh
6ZYeqj47nCMYKj0sCGl2IHm28FmvuH8h
oTDYRIA1trN1A2pQjsBwG3j9llzKIMhw
5zlpvSgTeLQ38eFWeSDoSY9IHEMHyzix

请注意,您可能会对它的运行速度感到惊讶。引擎盖下发生了很多事情。最后,这使用了 C++11 随机库,特别是均匀分布,它消除了传统 rand() % n 解决方案中通常遇到的特定 n 的模数偏差。

【讨论】:

  • 我宁愿做例如std::vector&lt;std::string&gt; strs{n},然后使用std::generate 直接设置条目,对字符串执行相同操作。与通过std::back_inserter 相比,可能会节省几个周期?
  • @JoachimPileborg 这是我使用reserve() 的原因。使用实际实例进行预置会触发所有构造函数(对于 std::string 来说,这并不多,但仍然如此)。最终是move-ctor vs move-assignment,所以如果它有所作为,我会感到惊讶。坐下来看看会很有趣。 编辑:我刚刚在您的评论中看到了generate。这将是非常有趣,看看它是否确实有显着差异。
【解决方案2】:

您可以考虑在 C++11 中提供 random number generators and distributions

例如,

const char alphanumeric[] = "0 .. 1A .. Za.. z";

std::default_random_engine rng;
std::uniform_int_distribution<> dist (0, sizeof(alphanumeric) - 1);

...

for (int i = 0; i < 32; i++)
    str += alphanumeric[dist(rng)];

我要补充一点,vecStr.push_back(str) 可能不会那么昂贵,因为它可能使用std::string 对象的移动分配std::string 对象在其实现中通常也具有“短字符串”优化 (SSO)。

vector<string> vecStr (N);
...
vecStr[index] = std::move(str);

【讨论】:

    【解决方案3】:

    您不能做得比O(mn) 更好(其中m 是字符串的长度(这里= 32),n 是字符串的数量)。

    原因是输出大小为O(mn),逻辑上需要为输出中的每个字符至少做O(1)工作。

    请注意,您的算法可能比O(mn) 慢一点,因为可能会发生一些字符串的重新分配。为了防止这种情况,你可以使用string::reserve:

    int M = 32;
    ...
      string str;
      str.reserve(M);
      for (int i = 0; i < M; ++i)
    ...
    

    但鉴于M 只有 32 岁,它不太可能产生重大影响。

    而且,只是为了好玩,这里是你的代码的一个变体:

    int N = 10000, M = 32;
    vector<string> vecStr;
    string alphabet("abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789");
    for (int index = 0; index < N; index++)
    {
      string str;
      str.reserve(M);
      for (int i = 0; i < M; ++i)
      {
        str += alphabet[rand() % alphabet.length()];
      }
      vecStr.push_back(str);
    }
    

    Live demo.

    【讨论】:

      【解决方案4】:

      考虑为您的随机字符串使用预先分配的缓冲区。 此外,您可能会预先生成一些随机块并排列它们。

      【讨论】:

        【解决方案5】:

        在算法效率方面并没有太大改善,但我建议

        void random_string(char *s, int len=32) {
        static const char alphabet[] =
            "0123456789"
            "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
            "abcdefghijklmnopqrstuvwxyz";
        
        for (int i = 0; i < len; ++i) {
            s[i] = alphabet[rand() % (sizeof(alphabet) - 1)];
          }
        
         s[len] = '\0';
        }
        

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 2010-09-08
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2021-04-05
          • 2011-08-18
          • 1970-01-01
          相关资源
          最近更新 更多