[a-zA-Z0-9]文字列のように:
na1dopW129T0anN28udaZ
または16進文字列:
8c6f78ac23b4a7b8c0182d
長い間、私は2K以上のキャラクターを意味します。
これは、私のボックスで約 200MBps を実行します。明らかに改善の余地があります。
type randomDataMaker struct {
src rand.Source
}
func (r *randomDataMaker) Read(p []byte) (n int, err error) {
for i := range p {
p[i] = byte(r.src.Int63() & 0xff)
}
return len(p), nil
}
io.CopyN
必要な文字列を生成するために使用するだけです。もちろん、途中でキャラクターセットを調整することもできます。
このモデルの良いところは、io.Reader
何でも作ることができるということです。
テストは以下です。
func BenchmarkRandomDataMaker(b *testing.B) {
randomSrc := randomDataMaker{rand.NewSource(1028890720402726901)}
for i := 0; i < b.N; i++ {
b.SetBytes(int64(i))
_, err := io.CopyN(ioutil.Discard, &randomSrc, int64(i))
if err != nil {
b.Fatalf("Error copying at %v: %v", i, err)
}
}
}
2.2GHz i7 の 1 つのコアで:
BenchmarkRandomDataMaker 50000 246512 ns/op 202.83 MB/s
編集
ベンチマークを作成したので、明らかな改善を行うことにしました (ランダムな呼び出しの頻度を減らします)。rand への呼び出しが 1/8 になると、実行速度は約 4 倍になりますが、かなり醜くなります。
新しいバージョン:
func (r *randomDataMaker) Read(p []byte) (n int, err error) {
todo := len(p)
offset := 0
for {
val := int64(r.src.Int63())
for i := 0; i < 8; i++ {
p[offset] = byte(val & 0xff)
todo--
if todo == 0 {
return len(p), nil
}
offset++
val >>= 8
}
}
panic("unreachable")
}
新しいベンチマーク:
BenchmarkRandomDataMaker 200000 251148 ns/op 796.34 MB/s
編集2
冗長だったので、バイトへのキャストでマスキングを取り除きました。かなり速くなりました:
BenchmarkRandomDataMaker 200000 231843 ns/op 862.64 MB/s
(これは実際の作業よりもはるかに簡単ですため息)
編集3
今日ircに出てきたのでライブラリを公開しました。また、私の実際のベンチマーク ツールは、相対速度には役立ちますが、レポートの精度が十分ではありません。
再利用して、必要な場所でランダム ストリームを生成できるrandboを作成しました。
Goパッケージuniuriを使用して、ランダムな文字列を生成できます(または、ソースコードを表示して、それらがどのように実行されているかを確認できます)。使用する必要があります:
func NewLen(length int) string
NewLenは、標準文字で構成される、指定された長さの新しいランダムな文字列を返します。
または、使用する文字のセットを指定するには、次のようにします。
func NewLenChars(length int, chars []byte) string
これは実際にはセットの最初の8文字に少し偏っています(255はの倍数ではないためlen(alphanum)
)が、これでほとんどの方法でそこに到達できます。
import (
"crypto/rand"
)
func randString(n int) string {
const alphanum = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz"
var bytes = make([]byte, n)
rand.Read(bytes)
for i, b := range bytes {
bytes[i] = alphanum[b % byte(len(alphanum))]
}
return string(bytes)
}
ここで、Evan Shaw の回答は、文字列の最初の 8 文字に偏ることなく作り直されました。多くの高価なbig.Int
操作を使用するため、おそらくそれほど高速ではないことに注意してください! しかし、答えはクリプトストロングです。
rand.Int
正確に正しいサイズの整数を作成するために使用しlen(alphanum) ** n
、実質的に base への base 変換を行いますlen(alphanum)
。
これには、より小さな剰余を保持し、必要に応じてランダムなバイトを追加することを含む、より優れたアルゴリズムがほぼ確実にあります。これにより、高価な長整数演算が不要になります。
import (
"crypto/rand"
"fmt"
"math/big"
)
func randString(n int) string {
const alphanum = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz"
symbols := big.NewInt(int64(len(alphanum)))
states := big.NewInt(0)
states.Exp(symbols, big.NewInt(int64(n)), nil)
r, err := rand.Int(rand.Reader, states)
if err != nil {
panic(err)
}
var bytes = make([]byte, n)
r2 := big.NewInt(0)
symbol := big.NewInt(0)
for i := range bytes {
r2.DivMod(r, symbols, symbol)
r, r2 = r2, r
bytes[i] = alphanum[symbol.Int64()]
}
return string(bytes)
}