Go で設定した長さのランダムな文字列をすばやく生成するにはどうすればよいですか?

Linda Hamilton
リリース: 2024-10-24 01:40:02
オリジナル
419 人が閲覧しました

How Do I Generate a Quick, Random String of a Set Length in Go?

Go で固定長のランダムな文字列を生成するにはどうすればよいですか?

問題

ランダムな文字列のみが必要です (大文字または小文字) )、Go では数字はありません。これを行う最も速くて簡単な方法は何ですか?

答え

この質問は、「最も速くて簡単な」アプローチを求めています。ポールの返答は簡単なテクニックを提供します。ただし、「最速」という側面も考慮してみましょう。コードを繰り返し改良して、最適化されたソリューションに到達します。

I.改善点

1.ジェネシス (ルーン)

最適化する最初のソリューションは次のとおりです:

<code class="go">import (
    "math/rand"
    "time"
)

var letterRunes = []rune("abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ")

func RandStringRunes(n int) string {
    b := make([]rune, n)
    for i := range b {
        b[i] = letterRunes[rand.Intn(len(letterRunes))]
    }
    return string(b)
}</code>
ログイン後にコピー
ログイン後にコピー

2.バイト

ランダムな文字列に使用される文字が英語のアルファベットの大文字と小文字に制限されている場合、英語のアルファベット文字は UTF-8 エンコードでバイトに 1 対 1 でマッピングされるため、バイトを扱うことができます ( Go が文字列を保存するために使用するもの)。

それで、

<code class="go">var letters = []rune("abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ")</code>
ログイン後にコピー
ログイン後にコピー

<code class="go">var letters = []byte("abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ")</code>
ログイン後にコピー
ログイン後にコピー

に置き換えることができます。さらに良いのは、

<code class="go">const letters = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"</code>
ログイン後にコピー

const を使用できるようになったので、これは大幅な改善です (Go は文字列定数をサポートしますが、スライス定数はサポートしません)。さらに、式 len(文字) も定数になります。

3. Remainder

以前のソリューションでは、rand.Intn() (Rand.Intn() に委任され、さらに Rand.Int31n() に委任されます) を呼び出すことで文字の乱数を決定していました。

これは、63 個のランダム ビットで乱数を生成する rand.Int63() を使用するよりも遅くなります。

したがって、単純に rand.Int63() を呼び出し、len(文字) で割った余りを使用できます。

<code class="go">func RandStringBytesRmndr(n int) string {
    b := make([]byte, n)
    for i := range b {
        b[i] = letters[rand.Int63() % int64(len(letters))]
    }
    return string(b)
}</code>
ログイン後にコピー

これは、すべての文字の等しい確率分布を維持しながら高速になります (歪みは無視できますが、文字数 52 は 1

4.マスキング

文字数を表すのに十分な乱数の最下位ビットのみを使用することで、文字の均等な分布を維持できます。 52 文字の場合、6 ビットが必要です: 52 = 110100b。したがって、 rand.Int63() によって返された数値の下位 6 ビットのみを使用します。

また、数値が 0..len(letterBytes)-1 の範囲内にある場合にのみ数値を「受け入れ」ます。 。最下位ビットの方が大きい場合は、破棄して新しい番号を要求します。

<code class="go">const (
    letterIdxBits = 6                    // 6 bits to represent a letter index
    letterIdxMask = 1<<letterIdxBits - 1 // All 1-bits, as many as letterIdxBits
)

func RandStringBytesMask(n int) string {
    b := make([]byte, n)
    for i := 0; i < n; {
        if idx := int(rand.Int63() & letterIdxMask); idx < len(letterBytes) {
            b[i] = letterBytes[idx]
            i++
        }
    }
    return string(b)
}
ログイン後にコピー

5.マスキングの改善

前のソリューションでは、rand.Int63() の 63 ランダム ビットのうちの下位 6 ビットのみが使用されます。ランダム ビットの取得がアルゴリズムの中で最も遅い部分であるため、これは非効率的です。

文字が 52 個あるため、6 ビットが文字インデックスをコード化します。 63 個のランダム ビットは、63/6 = 10 個の異なる文字インデックスを指定できます。

<code class="go">const (
    letterIdxBits = 6                    // 6 bits to represent a letter index
    letterIdxMask = 1<<letterIdxBits - 1 // All 1-bits, as many as letterIdxBits
    letterIdxMax  = 63 / letterIdxBits   // # of letter indices fitting in 63 bits
)

func RandStringBytesMaskImpr(n int) string {
    b := make([]byte, n)
    // A rand.Int63() generates 63 random bits, enough for letterIdxMax letters!
    for i, cache, remain := n-1, rand.Int63(), letterIdxMax; i >= 0; {
        if remain == 0 {
            cache, remain = rand.Int63(), letterIdxMax
        }
        if idx := int(cache & letterIdxMask); idx < len(letterBytes) {
            b[i] = letterBytes[idx]
            i--
        }
        cache >>= letterIdxBits
        remain--
    }
    return string(b)
}</code>
ログイン後にコピー

6の10個をすべて使いましょう。ソース

改善されたマスキング は非常に効率的です。別の側面、つまり乱数のソースについて考えてみましょう。

crypto/rand パッケージは関数 Read(b []byte) を提供します。ただし、crypto/rand は暗号的に安全な擬似乱数生成器を実装しており、速度が遅いため、これはパフォーマンスの向上にはつながりません。

そこで、math/rand パッケージを使用することにします。 rand.Rand は、ランダム ビットのソースとして rand.Source を使用します。したがって、 rand.Source を直接使用できます:

<code class="go">import (
    "math/rand"
    "time"
)

var letterRunes = []rune("abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ")

func RandStringRunes(n int) string {
    b := make([]rune, n)
    for i := range b {
        b[i] = letterRunes[rand.Intn(len(letterRunes))]
    }
    return string(b)
}</code>
ログイン後にコピー
ログイン後にコピー

7. strings.Builder

を利用すると、以前のソリューションでは、最初にスライスで構築された文字列 (Genesis では []rune、続いて []byte) が返され、その後文字列に変換されました。文字列値は不変であるため、この最終変換ではスライスの内容をコピーする必要があります。

Go 1.10 では strings.Builder が導入されました。この新しい型は、bytes.Buffer と同様に文字列コンテンツを構築するために使用できます。内部的に [] バイトを使用するため、文字列を生成するために内容をコピーする必要はありません。

<code class="go">var letters = []rune("abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ")</code>
ログイン後にコピー
ログイン後にコピー

8.パッケージ unsafe

strings.Builder を使用して strings.Builder を「模倣」すると、私たち自身が行ったのと同じように、内部の [] バイトに文字列が構築されます。したがって、strings.Builder を使用するとオーバーヘッドが発生しますが、これは最終的なコピーを避けるためにのみ切り替えました。

ただし、パッケージ unsafe:

<code class="go">var letters = []byte("abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ")</code>
ログイン後にコピー
ログイン後にコピー
を使用してこのコピーを回避することもできます。

以上がGo で設定した長さのランダムな文字列をすばやく生成するにはどうすればよいですか?の詳細内容です。詳細については、PHP 中国語 Web サイトの他の関連記事を参照してください。

ソース:php
このウェブサイトの声明
この記事の内容はネチズンが自主的に寄稿したものであり、著作権は原著者に帰属します。このサイトは、それに相当する法的責任を負いません。盗作または侵害の疑いのあるコンテンツを見つけた場合は、admin@php.cn までご連絡ください。
著者別の最新記事
人気のチュートリアル
詳細>
最新のダウンロード
詳細>
ウェブエフェクト
公式サイト
サイト素材
フロントエンドテンプレート
私たちについて 免責事項 Sitemap
PHP中国語ウェブサイト:福祉オンライン PHP トレーニング,PHP 学習者の迅速な成長を支援します!