«Если рабочий хочет хорошо выполнять свою работу, он должен сначала заточить свои инструменты» — Конфуций, «Аналитики Конфуция. Лу Лингун»
титульная страница > программирование > Как мне сгенерировать быструю случайную строку заданной длины в Go?

Как мне сгенерировать быструю случайную строку заданной длины в Go?

Опубликовано 14 ноября 2024 г.
Просматривать:444

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

Как сгенерировать случайную строку фиксированной длины в Go?

Проблема

Мне нужна только случайная строка символов (прописных или строчных букв) ), без цифр, в Go. Какой самый быстрый и простой способ сделать это?

Ответ

Вопрос ищет «самый быстрый и простой» подход. Ответ Пола предлагает простой метод. Однако давайте также рассмотрим «самый быстрый» аспект. Мы будем итеративно совершенствовать наш код, придя к оптимизированному решению.

I. Улучшения

1. Бытие (Руны)

Первоначальное решение, которое мы оптимизируем:

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)
}

2. Байты

Если символы, используемые для случайной строки, ограничены прописными и строчными буквами английского алфавита, мы можем работать с байтами, поскольку буквы английского алфавита сопоставляются 1-к-1 с байтами в кодировке UTF-8 ( который Go использует для хранения строк).

Таким образом, мы можем заменить:

var letters = []rune("abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ")

with:

var letters = []byte("abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ")

Или еще лучше:

const letters = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"

Это значительное улучшение, поскольку теперь мы можем использовать константу (Go поддерживает строковые константы, но не константы среза). Кроме того, выражение len(letters) также будет константой.

3. Remainder

Предыдущие решения определяли случайное число для буквы путем вызова rand.Intn() (который делегирует функции Rand.Intn() и далее Rand.Int31n()).

Это медленнее, чем использование rand.Int63(), которое генерирует случайное число из 63 случайных битов.

Так что мы можем просто вызвать rand.Int63() и используйте остаток от деления на len(letters):

func RandStringBytesRmndr(n int) string {
    b := make([]byte, n)
    for i := range b {
        b[i] = letters[rand.Int63() % int64(len(letters))]
    }
    return string(b)
}

Это быстрее, при сохранении равного распределения вероятностей всех букв (хотя искажение незначительно, количество букв 52 намного меньше, чем 1

4. Маскирование

Мы можем поддерживать равное распределение букв, используя только младшие биты случайного числа, достаточные для представления количества букв. Для 52 букв требуется 6 бит: 52 = 110100b. Поэтому мы будем использовать только младшие 6 бит числа, возвращаемого функцией rand.Int63().

Мы также «принимаем» число только в том случае, если оно попадает в диапазон 0..len(letterBytes)-1. . Если младшие биты больше, мы отбрасываем и запрашиваем новое число.

const (
    letterIdxBits = 6                    // 6 bits to represent a letter index
    letterIdxMask = 1

5. Улучшено маскирование

Предыдущее решение использует только младшие 6 бит из 63 случайных битов из rand.Int63(). Это неэффективно, поскольку получение случайных битов — самая медленная часть нашего алгоритма.

Поскольку у нас 52 буквы, 6 бит кодируют буквенный индекс. 63 случайных бита могут обозначать 63/6 = 10 различных буквенных индексов. Давайте использовать все 10:

const (
    letterIdxBits = 6                    // 6 bits to represent a letter index
    letterIdxMask = 1= 0; {
        if remain == 0 {
            cache, remain = rand.Int63(), letterIdxMax
        }
        if idx := int(cache & letterIdxMask); idx >= letterIdxBits
        remain--
    }
    return string(b)
}

6. Источник

Улучшенная маскировка весьма эффективна. Давайте рассмотрим другой аспект: источник случайных чисел.

Пакет crypto/rand предоставляет функцию Read(b []byte). Однако это не повысит производительность, поскольку crypto/rand реализует криптографически безопасный генератор псевдослучайных чисел, который работает медленнее.

Поэтому мы будем придерживаться пакета math/rand. rand.Rand использует rand.Source в качестве источника случайных битов. Таким образом, мы можем напрямую использовать rand.Source:

var src = rand.NewSource(time.Now().UnixNano())

func RandStringBytesMaskImprSrc(n int) string {
    b := make([]byte, n)
    // A src.Int63() generates 63 random bits, enough for letterIdxMax characters!
    for i, cache, remain := n-1, src.Int63(), letterIdxMax; i >= 0; {
        if remain == 0 {
            cache, remain = src.Int63(), letterIdxMax
        }
        if idx := int(cache & letterIdxMask); idx >= letterIdxBits
        remain--
    }
    return string(b)
}

7. Использование strings.Builder

Предыдущие решения возвращали строки, созданные сначала в срезе ([]руна в Бытие и []байт впоследствии), а затем преобразованные в строку. Это окончательное преобразование требует копирования содержимого фрагмента, поскольку строковые значения неизменяемы.

В Go 1.10 появились strings.Builder. Этот новый тип можно использовать для создания строкового содержимого аналогично bytes.Buffer. Он использует []байт внутри себя и не требует копирования содержимого для создания строки.

func RandStringBytesMaskImprSrcSB(n int) string {
    sb := strings.Builder{}
    sb.Grow(n)
    // A src.Int63() generates 63 random bits, enough for letterIdxMax characters!
    for i, cache, remain := n-1, src.Int63(), letterIdxMax; i >= 0; {
        if remain == 0 {
            cache, remain = src.Int63(), letterIdxMax
        }
        if idx := int(cache & letterIdxMask); idx >= letterIdxBits
        remain--
    }
    return sb.String()
}

8. «Имитация» строк.Builder с пакетом unsafe

strings.Builder строит строку во внутреннем []байте, как и мы сами. Таким образом, использование strings.Builder приводит к некоторым накладным расходам, которые мы исключили, чтобы избежать окончательного копирования.

Однако мы также можем избежать этого копирования, используя unsafe пакета:

func RandStringBytesMaskImprSrcUnsafe(n int) string {
    b := make([]byte, n)
    // A src.Int63() generates 63 random bits, enough for letterIdxMax characters!
    for i, cache, remain := n-1, src.Int63(), letterIdxMax; i >= 0; {
        if remain == 0 {
            cache, remain = src.Int63(), letterIdxMax
        }
        if idx := int(cache & letterIdxMask); idx 
Заявление о выпуске Эта статья воспроизводится: 1729661378 Если есть какие -либо нарушения, пожалуйста, свяжитесь с учебным заведением[email protected], чтобы удалить его.
Последний учебник Более>

Изучайте китайский

Отказ от ответственности: Все предоставленные ресурсы частично взяты из Интернета. В случае нарушения ваших авторских прав или других прав и интересов, пожалуйста, объясните подробные причины и предоставьте доказательства авторских прав или прав и интересов, а затем отправьте их по электронной почте: [email protected]. Мы сделаем это за вас как можно скорее.

Copyright© 2022 湘ICP备2022001581号-3