我只想要一個隨機字串(大寫或小寫) ),在 Go 中沒有數字。最快、最簡單的方法是什麼?
問題尋求「最快、最簡單」的方法。保羅的回應提供了一個簡單的技巧。然而,我們也考慮一下「最快」的方面。我們將迭代地改進我們的程式碼,得出優化的解決方案。
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(letters) 也將是常數。
3.餘數
先前的解決方案透過呼叫rand.Intn() (委託給Rand.Intn() 並進一步委託給Rand.Int31n())來決定字母的隨機數。 這比使用 rand.Int63() 產生具有 63 個隨機位元的隨機數要慢。<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>
因此我們可以簡單地調用rand.Int63() 並使用除以len(letters) 後的餘數:
這樣速度更快,同時保持所有字母的機率分佈相等(雖然失真可以忽略不計,但字母數量52 遠小於1
4.掩蔽
我們可以透過僅使用足以表示字母數量的隨機數的最低位來保持字母的均勻分佈。對於 52 個字母,需要 6 位:52 = 110100b。因此,我們將僅使用 rand.Int63() 傳回的數字的最低 6 位元。<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) }
我們也只「接受」落在 0..len(letterBytes)-1 範圍內的數字。如果最低位較大,我們將丟棄並請求一個新數字。
5。屏蔽改進
先前的解決方案僅使用 rand.Int63() 中 63 個隨機位元中的最低 6 位元。這是低效的,因為取得隨機位元是我們演算法中最慢的部分。<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>
由於我們有 52 個字母,因此 6 位元編碼一個字母索引。 63 個隨機位元可以指定 63/6 = 10 個不同的字母索引。讓我們使用全部 10 個:
6。來源
屏蔽改進非常有效。我們再考慮另一個面向:隨機數的來源。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 的字串內容。它內部使用 []byte,不需要複製內容來產生字串。
<code class="go">var letters = []rune("abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ")</code>
8.使用套件unsafe
strings.Builder 來「模仿」 strings.Builder 在內部[] 位元組中建構一個字串,就像我們自己做的那樣。因此,使用 strings.Builder 會帶來一些開銷,我們只是為了避免最終的複製而切換。
但是,我們也可以使用 package unsafe 來避免這種複製:
<code class="go">var letters = []byte("abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ")</code>
以上是如何在 Go 中快速產生指定長度的隨機字串?的詳細內容。更多資訊請關注PHP中文網其他相關文章!