15

[a-zA-Z0-9]文字列のように:

na1dopW129T0anN28udaZ

または16進文字列:

8c6f78ac23b4a7b8c0182d

長い間、私は2K以上のキャラクターを意味します。

4

5 に答える 5

23

これは、私のボックスで約 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を作成しました。

于 2012-10-10T00:26:46.503 に答える
22

Goパッケージuniuriを使用して、ランダムな文字列を生成できます(または、ソースコードを表示して、それらがどのように実行されているかを確認できます)。使用する必要があります:

func NewLen(length int) string

NewLenは、標準文字で構成される、指定された長さの新しいランダムな文字列を返します。

または、使用する文字のセットを指定するには、次のようにします。

func NewLenChars(length int, chars []byte) string
于 2012-10-07T20:43:55.160 に答える
16

これは実際にはセットの最初の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)
}
于 2012-10-07T20:37:38.890 に答える
1

ここで、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)
}
于 2012-10-09T07:57:47.850 に答える