在Go语言中,我只需要一个随机字符串(大写或小写),没有数字。 最快和最简单的方法是什么?
Paul的解决方案提供了一个简单的通用解决方案。
好。
这个问题要求"最快和最简单的方法"。让我们也讨论最快的部分。我们将以迭代的方式得出最终的最快的代码。对每个迭代进行基准测试可以在答案的结尾处找到。
好。
所有解决方案和基准代码都可以在Go Playground上找到。 Playground上的代码是测试文件,而不是可执行文件。您必须将其保存到名为
好。
前言:
好。
The fastest solution is not a go-to solution if you just need a random string. For that, Paul's solution is perfect. This is if performance does matter. Although the first 2 steps (Bytes and Remainder) might be an acceptable compromise: they do improve performance by like 50% (see exact numbers in the II. Benchmark section), and they don't increase complexity significantly.
Ok.
话虽如此,即使您不需要最快的解决方案,通读此答案也可能是冒险和有益的。
好。
一,改进
1.创世记(符文)
提醒一下,我们正在改进的原始通用解决方案是:
好。
2.字节
如果要从中选择和组合随机字符串的字符仅包含英文字母的大写和小写字母,我们只能使用字节,因为英文字母映射为UTF-8编码中的1-to-1字节(是Go存储字符串的方式)。
好。
所以代替:
好。
我们可以用:
好。
甚至更好:
好。
现在,这已经是一个很大的改进:我们可以将其实现为
好。
而且要花多少钱?没事可以对
好。
我们的下一个目的地如下所示:
好。
3.剩余
先前的解决方案通过调用将
好。
与
好。
因此,我们可以简单地调用
好。
这行之有效,而且速度更快,缺点是所有字母的概率不会完全相同(假设
好。
为了使理解更容易:假设您想要一个在
好。
4.遮罩
在以前的解决方案的基础上,我们可以通过使用与代表字母数量所需的数量一样多的随机数最低位来维持字母的均等分布。因此,例如,如果我们有52个字母,则需要6位才能表示它:
好。
请注意,最低位通常大于或等于
好。
所以这是解决方案:
好。
5.改进了遮罩
先前的解决方案仅使用
好。
如果我们有52个字母,则意味着6个位编码一个字母索引。因此63个随机位可以指定
好。
6.来源
改进的遮罩效果非常好,我们可以对其进行改进。我们可以,但不值得如此复杂。
好。
现在让我们找到其他需要改进的地方。随机数的来源。
好。
有一个
好。
因此,让我们坚持使用
好。
因此,我们实际上并不需要
好。
还要注意,最后一个解决方案不需要您初始化(种子)
好。
这里还要注意一件事:
好。
The default Source is safe for concurrent use by multiple goroutines.
Ok.
因此,默认源比
好。
7.使用
以前的所有解决方案均返回
好。
Go 1.10引入了
好。
因此,我们的下一个想法是不要在切片中构建随机字符串,而是借助
好。
请注意,在创建新的
好。
8.使用包
好。
好。
关键是,我们也可以自己做。因此,这里的想法是切换回在
好。
这是可以做到的:
好。
(9.使用
Go 1.7添加了
好。
这有一个小"问题":我们需要多少个字节?我们可以说:与输出字母的数量一样多。我们认为这是一个较高的估计,因为字母索引使用的少于8位(1个字节)。但是在这一点上,我们已经变得更糟了(因为获得随机位是"困难的部分"),而且我们得到的超出了需要。
好。
还要注意,为了保持所有字母索引的均等分布,可能会有一些我们将无法使用的"垃圾"随机数据,因此我们最终将跳过一些数据,因此当我们遍历所有数据时会变得很短。字节片。我们将需要进一步"递归"获得更多随机字节。现在我们甚至失去了"对
好。
我们可以"某种程度上"优化从
好。
但是基准代码仍然表明我们没有赢。为什么会这样呢?
好。
最后一个问题的答案是,因为
好。
二。基准测试
好的,现在是对不同解决方案进行基准测试的时候了。
好。
关键时刻:
好。
只需从符文转换为字节,我们即可立即获得24%的性能提升,而内存需求则下降至三分之一。
好。
摆脱
好。
遮罩(在大索引的情况下重复)会稍微减慢(由于重复调用):-22%...
好。
但是,当我们利用所有63个随机位(或大多数)(从一个
好。
如果我们使用(非默认值,新的)
好。
如果使用
好。
最后,如果我们敢使用包
好。
将最终解决方案与初始解决方案进行比较:
好。
好。
您可以为此编写代码。如果要以UTF-8编码时都依赖于全部为单个字节的字母,则此代码可以更简单一些。
两种可能的选择(当然可能还有更多选择):
您可以使用
取一个随机数,然后使用md5或类似的方式对其进行哈希处理。
使用软件包uniuri,该软件包将生成加密安全的统一(无偏)字符串。
免责声明:我是包裹的作者
在
如果您想要一个更通用的解决方案,该解决方案允许您传入字符字节片以创建字符串,则可以尝试使用以下方法:
如果您想传递自己的随机性来源,则修改上面的内容以接受
如果要使用密码安全的随机数,并且确切的字符集是灵活的(例如,base64很好),则可以从所需的输出大小中准确计算出所需的随机字符长度。
基数64的文本比基数256长1/3。(2 ^ 8 vs 2 ^ 6; 8bits / 6bits = 1.333 ratio)
注意:如果您喜欢+和/而不是-和_,也可以使用RawStdEncoding
如果要使用十六进制,则基数16比基数256长2倍。(2 ^ 8与2 ^ 4; 8bits / 4bits = 2x ratio)
但是,如果您的字符集具有从base256到baseN的编码器,则可以将其扩展到任意字符集。您可以使用相同的大小进行计算,以表示字符集需要多少位。任何任意字符集的比率计算为:
尽管这两种解决方案都是安全,简单,应该快速的,并且不会浪费您的加密熵池。
这是操场显示它适用于任何大小。 https://play.golang.org/p/i61WUVR8_3Z
这是我的方式)随意使用数学兰特或加密兰特。
如果您愿意在允许的字符池中添加一些字符,则可以使该代码与通过io.Reader提供随机字节的任何内容一起使用。在这里,我们使用
BenchmarkRandStr16-8 20000000 68.1 ns / op 16 B / op 1 allocs / op
此外,我还找到了一个包,其中包含一堆用于处理假数据的方法。发现它在开发https://github.com/Pallinder/go-randomdata时对播种数据库很有用。可能对其他人也有帮助