@VinceEmigh: Ini topik meta yang membahas pertanyaan dasar. meta.stackoverflow.com/q/274645/395461 Secara pribadi, saya pikir pertanyaan dasar tidak masalah jika ditulis dengan baik dan sesuai topik. Lihatlah jawaban di bawah ini, mereka menggambarkan banyak hal yang akan berguna bagi orang baru untuk pergi. Untuk loop, ketik casting, make (), dll.
Shannon Matthews
2
@Shannon " Pertanyaan ini tidak menunjukkan upaya penelitian apa pun " (jawaban yang sangat tinggi di tautan Anda) - Itulah yang saya maksud. Dia tidak menunjukkan upaya penelitian. Tidak ada usaha sama sekali (upaya, atau bahkan menyatakan bahwa dia mencari online, yang jelas tidak dia lakukan). Meskipun akan bermanfaat bagi orang baru , situs ini tidak berfokus untuk mengajar orang baru. Ini berfokus pada menjawab masalah / pertanyaan pemrograman tertentu, bukan tutorial / panduan. Meskipun dapat digunakan untuk yang terakhir, itu bukan fokus, dan dengan demikian pertanyaan ini harus ditutup. Sebagai gantinya, ini spoonfed /:
Vince Emigh
9
@VinceEmigh Saya mengajukan pertanyaan ini setahun yang lalu. Saya telah mencari online untuk string acak dan membaca dokumen juga. Tapi itu tidak membantu. Jika saya belum menulis dalam pertanyaan, maka itu tidak berarti saya belum meneliti.
Pertanyaannya menanyakan "cara tercepat dan termudah" . Mari kita membahas bagian tercepat juga. Kami akan tiba di kode final kami yang tercepat dengan cara berulang-ulang. Benchmarking setiap iterasi dapat ditemukan di akhir jawaban.
Semua solusi dan kode pembandingan dapat ditemukan di Go Playground . Kode di Playground adalah file tes, bukan yang dapat dieksekusi. Anda harus menyimpannya ke dalam file bernama XX_test.godan menjalankannya
go test -bench .-benchmem
Kata Pengantar :
Solusi tercepat bukanlah solusi masuk jika Anda hanya membutuhkan string acak. Untuk itu, solusi Paul sangat sempurna. Ini jika kinerja memang penting. Meskipun 2 langkah pertama ( Bytes dan Remainder ) mungkin merupakan kompromi yang dapat diterima: mereka memang meningkatkan kinerja sebesar 50% (lihat angka pasti di bagian II. Benchmark ), dan mereka tidak meningkatkan kompleksitas secara signifikan.
Karena itu, bahkan jika Anda tidak membutuhkan solusi tercepat, membaca jawaban ini mungkin penuh petualangan dan mendidik.
I. Perbaikan
1. Genesis (Rune)
Sebagai pengingat, solusi umum asli yang kami tingkatkan adalah ini:
func init(){
rand.Seed(time.Now().UnixNano())}var letterRunes =[]rune("abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ")
func RandStringRunes(n int)string{
b := make([]rune, n)for i := range b {
b[i]= letterRunes[rand.Intn(len(letterRunes))]}returnstring(b)}
2. Bytes
Jika karakter untuk memilih dari dan merangkai string acak hanya berisi huruf besar dan kecil dari alfabet bahasa Inggris, kita dapat bekerja dengan byte hanya karena huruf alfabet Inggris memetakan ke byte 1-ke-1 dalam pengkodean UTF-8 (yang adalah cara Go menyimpan string).
Jadi alih-alih:
var letters =[]rune("abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ")
kita bisa menggunakan:
var letters =[]bytes("abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ")
Sekarang ini sudah merupakan perbaikan besar: kita bisa mencapainya menjadi const(ada stringkonstanta tetapi tidak ada konstanta slice ). Sebagai keuntungan tambahan, ekspresi len(letters)juga akan menjadi const! (Ekspresi len(s)konstan jika sstring konstan.)
Dan berapa biayanya? Tidak ada sama sekali. strings dapat diindeks yang mengindeks byte-nya, sempurna, persis seperti yang kita inginkan.
Tujuan kami selanjutnya terlihat seperti ini:
const letterBytes ="abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"
func RandStringBytes(n int)string{
b := make([]byte, n)for i := range b {
b[i]= letterBytes[rand.Intn(len(letterBytes))]}returnstring(b)}
3. Sisa
Solusi sebelumnya mendapatkan nomor acak untuk menunjuk surat acak dengan memanggil rand.Intn()delegasi Rand.Intn()mana yang akan didelegasikan Rand.Int31n().
Ini jauh lebih lambat dibandingkan dengan rand.Int63()yang menghasilkan angka acak dengan 63 bit acak.
Jadi kita bisa memanggil rand.Int63()dan menggunakan sisanya setelah membaginya dengan len(letterBytes):
func RandStringBytesRmndr(n int)string{
b := make([]byte, n)for i := range b {
b[i]= letterBytes[rand.Int63()% int64(len(letterBytes))]}returnstring(b)}
Ini bekerja dan secara signifikan lebih cepat, kerugiannya adalah bahwa probabilitas semua huruf tidak akan persis sama (dengan asumsi rand.Int63()menghasilkan semua angka 63-bit dengan probabilitas yang sama). Meskipun distorsi sangat kecil karena jumlah huruf 52jauh lebih kecil daripada 1<<63 - 1, jadi dalam praktiknya ini baik-baik saja.
Untuk membuat ini lebih mudah dipahami: katakanlah Anda ingin nomor acak dalam kisaran 0..5. Menggunakan 3 bit acak, ini akan menghasilkan angka 0..1dengan probabilitas ganda daripada dari kisaran 2..5. Dengan menggunakan 5 bit acak, angka dalam kisaran 0..1akan muncul dengan 6/32probabilitas dan angka dalam kisaran 2..5dengan 5/32probabilitas yang sekarang lebih dekat ke yang diinginkan. Meningkatkan jumlah bit membuat ini kurang signifikan, ketika mencapai 63 bit, dapat diabaikan.
4. Masking
Membangun pada solusi sebelumnya, kita dapat mempertahankan distribusi huruf yang sama dengan hanya menggunakan banyak bit terendah dari angka acak sebanyak yang diperlukan untuk mewakili jumlah huruf. Jadi misalnya jika kita memiliki 52 huruf, membutuhkan 6 bit untuk mewakilinya: 52 = 110100b. Jadi kita hanya akan menggunakan 6 bit terendah dari angka yang dikembalikan oleh rand.Int63(). Dan untuk menjaga pemerataan surat, kami hanya "menerima" nomor itu jika berada dalam kisaran 0..len(letterBytes)-1. Jika bit terendah lebih besar, kami membuangnya dan meminta nomor acak baru.
Perhatikan bahwa peluang bit terendah untuk lebih besar dari atau sama dengan len(letterBytes)kurang dari 0.5pada umumnya ( 0.25rata-rata), yang berarti bahwa bahkan jika ini yang terjadi, mengulangi kasus "langka" ini mengurangi kemungkinan tidak menemukan barang yang baik. jumlah. Setelah npengulangan, kemungkinan kita masih tidak memiliki indeks yang baik adalah jauh lebih sedikit daripada pow(0.5, n), dan ini hanya perkiraan yang lebih tinggi. Dalam hal 52 huruf kemungkinan 6 bit terendah tidak bagus hanya (64-52)/64 = 0.19; yang artinya misalnya peluang untuk tidak memiliki angka yang baik setelah 10 kali pengulangan 1e-8.
Jadi, inilah solusinya:
const letterBytes ="abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"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++}}returnstring(b)}
5. Masking Ditingkatkan
Solusi sebelumnya hanya menggunakan 6 bit terendah dari 63 bit acak yang dikembalikan oleh rand.Int63(). Ini adalah pemborosan karena mendapatkan bit acak adalah bagian paling lambat dari algoritma kami.
Jika kita memiliki 52 huruf, itu berarti 6 bit kode indeks huruf. Jadi 63 bit acak dapat menunjuk 63/6 = 10indeks huruf yang berbeda. Mari kita gunakan semua 10 itu:
const letterBytes ="abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"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--}returnstring(b)}
6. Sumber
The Masking Peningkatan ini cukup bagus, tidak banyak yang bisa kita memperbaiki itu. Kita bisa, tetapi tidak sebanding dengan kerumitannya.
Sekarang mari kita cari hal lain untuk diperbaiki. Sumber angka acak.
Ada crypto/randpaket yang menyediakan Read(b []byte)fungsi, jadi kita bisa menggunakannya untuk mendapatkan banyak byte dengan satu panggilan sebanyak yang kita butuhkan. Ini tidak akan membantu dalam hal kinerja karena crypto/randmengimplementasikan generator nomor pseudorandom yang aman secara kriptografis sehingga jauh lebih lambat.
Jadi mari kita tetap berpegang pada math/randpaket. The rand.Randmenggunakan rand.Sourcesebagai sumber bit acak. rand.Sourceadalah antarmuka yang menentukan Int63() int64metode: tepat dan satu-satunya hal yang kami butuhkan dan gunakan dalam solusi terbaru kami.
Jadi kita tidak benar-benar membutuhkan rand.Rand(baik eksplisit atau global, berbagi salah satu randpaket), a rand.Sourcesudah cukup sempurna bagi kita:
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 < len(letterBytes){
b[i]= letterBytes[idx]
i--}
cache >>= letterIdxBits
remain--}returnstring(b)}
Juga catatan bahwa solusi terakhir ini tidak mengharuskan Anda untuk menginisialisasi (benih) global Randdari math/randpaket sebagai yang tidak digunakan (dan kami rand.Sourcebenar diinisialisasi / unggulan).
Satu hal lagi yang perlu diperhatikan di sini: paket dokumen math/randnegara:
Sumber default aman untuk digunakan bersamaan oleh beberapa goroutine.
Jadi sumber default lebih lambat dari Sourceyang dapat diperoleh rand.NewSource(), karena sumber default harus memberikan keamanan di bawah akses / penggunaan bersamaan, sementara rand.NewSource()tidak menawarkan ini (dan dengan demikian Sourcedikembalikan oleh lebih mungkin lebih cepat).
7. Memanfaatkan strings.Builder
Semua solusi sebelumnya menghasilkan stringkonten yang pertama kali dibuat dalam slice ( []runedalam Genesis , dan []bytedalam solusi berikutnya), dan kemudian dikonversi menjadi string. Konversi akhir ini harus membuat salinan konten slice, karena stringnilainya tidak berubah, dan jika konversi tidak akan membuat salinan, tidak dapat dijamin bahwa konten string tidak dimodifikasi melalui slice aslinya. Untuk detailnya, lihat Bagaimana mengonversi utf8 string ke [] byte? dan golang: [] byte (string) vs [] byte (* string) .
Go 1.10 diperkenalkan strings.Builder. strings.Buildertipe baru yang bisa kita gunakan untuk membangun konten yang stringmirip dengan bytes.Buffer. Itu melakukannya secara internal menggunakan []byte, dan ketika kita selesai, kita bisa mendapatkan nilai akhir stringmenggunakan ituBuilder.String() metodenya. Tapi apa yang keren di dalamnya adalah ia melakukan ini tanpa melakukan salinan yang baru saja kita bicarakan di atas. Ia berani melakukannya karena irisan byte yang digunakan untuk membangun konten string tidak diekspos, sehingga dijamin tidak ada yang dapat memodifikasinya secara tidak sengaja atau jahat untuk mengubah string "immutable" yang dihasilkan.
Jadi ide kami berikutnya adalah untuk tidak membangun string acak dalam sebuah irisan, tetapi dengan bantuan a strings.Builder, jadi setelah kami selesai, kami dapat memperoleh dan mengembalikan hasilnya tanpa harus membuat salinannya. Ini mungkin membantu dalam hal kecepatan, dan pasti akan membantu dalam hal penggunaan dan alokasi memori.
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 < len(letterBytes){
sb.WriteByte(letterBytes[idx])
i--}
cache >>= letterIdxBits
remain--}return sb.String()}
Perhatikan bahwa setelah membuat yang baru strings.Buidler, kami memanggil Builder.Grow()metodenya, memastikannya mengalokasikan irisan internal yang cukup besar (untuk menghindari realokasi saat kami menambahkan huruf acak).
8. "Meniru" strings.Builderdengan paketunsafe
strings.Buildermembangun string di internal []byte, sama seperti yang kita lakukan sendiri. Jadi pada dasarnya melakukannya melalui strings.Builderbeberapa overhead, satu-satunya hal yang kita beralih strings.Builderadalah untuk menghindari penyalinan akhir dari slice.
strings.Buildermenghindari salinan akhir dengan menggunakan paket unsafe:
// String returns the accumulated string.
func (b *Builder)String()string{return*(*string)(unsafe.Pointer(&b.buf))}
Masalahnya, kita juga bisa melakukan ini sendiri. Jadi idenya di sini adalah untuk kembali ke membangun string acak dalam []byte, tetapi ketika kita selesai, jangan mengubahnya stringuntuk kembali, tetapi lakukan konversi yang tidak aman: dapatkan stringyang menunjuk ke byte slice kami sebagai data string .
Ini adalah bagaimana hal itu dapat dilakukan:
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 < len(letterBytes){
b[i]= letterBytes[idx]
i--}
cache >>= letterIdxBits
remain--}return*(*string)(unsafe.Pointer(&b))}
(9. Menggunakan rand.Read())
Go 1.7 menambahkan suatu rand.Read()fungsi danRand.Read() metode. Kita harus tergoda untuk menggunakan ini untuk membaca byte sebanyak yang kita butuhkan dalam satu langkah, untuk mencapai kinerja yang lebih baik.
Ada satu "masalah" kecil dengan ini: berapa banyak byte yang kita butuhkan? Kita bisa mengatakan: sebanyak jumlah surat keluaran. Kami akan berpikir ini adalah estimasi atas, karena indeks huruf menggunakan kurang dari 8 bit (1 byte). Tetapi pada titik ini kita sudah melakukan yang lebih buruk (karena mendapatkan bit acak adalah "bagian yang sulit"), dan kita mendapatkan lebih dari yang dibutuhkan.
Juga perhatikan bahwa untuk menjaga distribusi yang sama dari semua indeks huruf, mungkin ada beberapa data acak "sampah" yang tidak dapat kami gunakan, jadi kami akan melewatkan beberapa data, dan dengan demikian berakhir pendek ketika kami memeriksa semua potongan byte. Kita perlu mendapatkan lebih banyak byte acak, "secara rekursif". Dan sekarang kita bahkan kehilangan keuntungan "satu panggilan ke randpaket" ...
Kami dapat "agak" mengoptimalkan penggunaan data acak yang kami peroleh math.Rand(). Kami dapat memperkirakan berapa banyak byte (bit) yang kami butuhkan. 1 huruf membutuhkan letterIdxBitsbit, dan kita perlu nhuruf, jadi kita perlu n * letterIdxBits / 8.0byte yang mengumpulkan. Kami dapat menghitung probabilitas indeks acak tidak dapat digunakan (lihat di atas), jadi kami dapat meminta lebih banyak yang "lebih mungkin" cukup (jika ternyata tidak, kami ulangi prosesnya). Kita dapat memproses byte slice sebagai "bit stream" misalnya, yang kami punya lib pihak ke-3 yang bagus: github.com/icza/bitio(pengungkapan: Saya penulisnya).
Namun kode Benchmark masih menunjukkan kita tidak menang. Kenapa gitu?
Jawaban untuk pertanyaan terakhir adalah karena rand.Read()menggunakan perulangan dan terus memanggil Source.Int63()hingga mengisi irisan yang dilewati. Apa yang dilakukan RandStringBytesMaskImprSrc()solusinya, tanpa buffer perantara, dan tanpa kompleksitas tambahan. Itu sebabnya RandStringBytesMaskImprSrc()tetap di atas takhta. Ya, RandStringBytesMaskImprSrc()gunakan yang tidak disinkronkan rand.Sourceseperti rand.Read(). Namun alasannya masih berlaku; dan yang terbukti jika kita menggunakan Rand.Read()bukan rand.Read()(yang sebelumnya juga tidak disinkronkan).
II Tolok ukur
Baiklah, sudah waktunya untuk membandingkan solusi yang berbeda.
Hanya dengan beralih dari rune ke byte, kami langsung memiliki peningkatan kinerja 24% , dan kebutuhan memori turun menjadi sepertiga .
Menyingkirkan rand.Intn()dan menggunakan rand.Int63()sebagai gantinya memberikan 20% lagi .
Masking (dan mengulangi jika indeks besar) sedikit melambat (karena panggilan pengulangan): -22% ...
Tetapi ketika kita menggunakan semua (atau sebagian besar) dari 63 bit acak (10 indeks dari satu rand.Int63()panggilan): yang mempercepat waktu besar: 3 kali .
Jika kita puas dengan (non-default, baru) rand.Sourcebukan rand.Rand, kita lagi memperoleh 21%.
Jika kita menggunakan strings.Builder, kita memperoleh kecil 3,5% di kecepatan , tapi kami juga mencapai 50% pengurangan penggunaan memori dan alokasi! Itu bagus!
Akhirnya jika kita berani menggunakan paket, unsafebukan strings.Builder, kita lagi mendapatkan 14% yang bagus .
Membandingkan final dengan solusi awal: RandStringBytesMaskImprSrcUnsafe()adalah 6,3 kali lebih cepat daripada RandStringRunes(), menggunakan memori keenam dan alokasi setengah . Misi selesai.
@RobbieV Yup, karena rand.Sourcedigunakan bersama . Solusi yang lebih baik adalah dengan meneruskan rand.Sourceke RandStringBytesMaskImprSrc()fungsi, dan dengan demikian tidak diperlukan penguncian dan karenanya kinerja / efisiensi tidak terpengaruh. Setiap goroutine bisa memiliki sendiri Source.
icza
113
@icza, itu adalah salah satu jawaban terbaik yang saya lihat sejak lama di SO!
@ ZanLynx thx untuk tip; meskipun deferuntuk membuka kunci mutex baik segera sebelum atau setelah memanggil kunci adalah IMO sebagian besar adalah ide yang sangat bagus; Anda dijamin untuk keduanya tidak lupa untuk membuka kunci tetapi juga membuka kunci meskipun dalam fungsi panik yang tidak fatal.
Mike Atlas
1
@RobbieV sepertinya kode ini aman untuk thread / goroutine karena sumber bersama yang mendasarinya sudah merupakan LockedSource yang mengimplementasikan mutex ( golang.org/src/math/rand/rand.rand:25go ).
adityajones
130
Anda bisa menulis kode untuk itu. Kode ini bisa sedikit lebih sederhana jika Anda ingin mengandalkan huruf-huruf yang semuanya byte tunggal ketika dikodekan dalam UTF-8.
package main
import("fmt""time""math/rand")var letters =[]rune("abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ")
func randSeq(n int)string{
b := make([]rune, n)for i := range b {
b[i]= letters[rand.Intn(len(letters))]}returnstring(b)}
func main(){
rand.Seed(time.Now().UnixNano())
fmt.Println(randSeq(10))}
Jangan lupa tentang rand.Seed (), jika tidak, Anda mendapatkan string yang sama setiap kali pertama kali diluncurkan ... rand.Seed (waktu. Sekarang (). UTC (). UnixNano ())
Evan Lin
2
Penambahan Evan benar, namun ada opsi lain yang serupa: rand.Seed(time.Now().Unix())ataurand.Seed(time.Now().UnixNano())
openwonk
7
Untuk rahasia yang sulit ditebak - kata sandi, kunci kripto, dll-- jangan pernah gunakan math/rand; gunakan crypto/rand(seperti opsi @ Not_A_Golfer 1).
twotwotwo
1
@ EvanLin Apakah ini tidak bisa ditebak? Jika saya harus menyemai generator, maka penyerang bisa menebak waktu saya menyemai dan memprediksi output yang sama yang saya hasilkan.
Matej
4
Perhatikan bahwa jika Anda mencoba program di atas dengan seed, on go playground, itu akan menghasilkan hasil yang sama setiap saat. Saya mencobanya di taman bermain dan setelah beberapa waktu menyadari ini. Kalau tidak, itu bekerja dengan baik untuk saya. Semoga menghemat waktu seseorang :)
Gaurav Sinha
18
Gunakan paket uniuri , yang menghasilkan string seragam (tidak bias) yang aman secara kriptografis.
Selain itu, penulis, dchest, adalah pengembang yang sangat baik dan telah menghasilkan sejumlah paket kecil yang bermanfaat seperti ini.
Roshambo
16
Dua opsi yang mungkin (tentu saja ada lebih banyak):
Anda dapat menggunakan crypto/randpaket yang mendukung membaca array byte acak (dari / dev / urandom) dan diarahkan untuk pembuatan acak kriptografis. lihat http://golang.org/pkg/crypto/rand/#example_Read . Mungkin lebih lambat dari generasi nomor pseudo-acak normal.
Ambil nomor acak dan hash menggunakan md5 atau sesuatu seperti ini.
Mengikuti icza'ssolusi yang dijelaskan dengan luar biasa, berikut ini adalah modifikasi yang menggunakan crypto/randalih-alih math/rand.
const(
letterBytes ="abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"// 52 possibilities
letterIdxBits =6// 6 bits to represent 64 possibilities / indexes
letterIdxMask =1<<letterIdxBits -1// All 1-bits, as many as letterIdxBits)
func SecureRandomAlphaString(length int)string{
result := make([]byte, length)
bufferSize :=int(float64(length)*1.3)for i, j, randomBytes :=0,0,[]byte{}; i < length; j++{if j%bufferSize ==0{
randomBytes =SecureRandomBytes(bufferSize)}if idx :=int(randomBytes[j%length]& letterIdxMask); idx < len(letterBytes){
result[i]= letterBytes[idx]
i++}}returnstring(result)}// SecureRandomBytes returns the requested number of bytes using crypto/rand
func SecureRandomBytes(length int)[]byte{var randomBytes = make([]byte, length)
_, err := rand.Read(randomBytes)if err !=nil{
log.Fatal("Unable to generate random bytes")}return randomBytes
}
Jika Anda menginginkan solusi yang lebih umum, yang memungkinkan Anda meneruskan potongan byte karakter untuk membuat string keluar, Anda dapat mencoba menggunakan ini:
// SecureRandomString returns a string of the requested length,// made from the byte characters provided (only ASCII allowed).// Uses crypto/rand for security. Will panic if len(availableCharBytes) > 256.
func SecureRandomString(availableCharBytes string, length int)string{// Compute bitMask
availableCharLength := len(availableCharBytes)if availableCharLength ==0|| availableCharLength >256{
panic("availableCharBytes length must be greater than 0 and less than or equal to 256")}var bitLength bytevar bitMask bytefor bits := availableCharLength -1; bits !=0;{
bits = bits >>1
bitLength++}
bitMask =1<<bitLength -1// Compute bufferSize
bufferSize := length + length /3// Create random string
result := make([]byte, length)for i, j, randomBytes :=0,0,[]byte{}; i < length; j++{if j%bufferSize ==0{// Random byte buffer is empty, get a new one
randomBytes =SecureRandomBytes(bufferSize)}// Mask bytes to get an index into the character sliceif idx :=int(randomBytes[j%length]& bitMask); idx < availableCharLength {
result[i]= availableCharBytes[idx]
i++}}returnstring(result)}
Jika Anda ingin meneruskan sumber keacakan Anda sendiri, akan sepele untuk memodifikasi di atas untuk menerima io.Readeralih - alih menggunakan crypto/rand.
jika kamu mau angka acak yang aman secara kriptografis , dan rangkaian karakter yang tepat fleksibel (misalnya, base64 baik-baik saja), Anda dapat menghitung dengan tepat berapa panjang karakter acak yang Anda butuhkan dari ukuran output yang diinginkan.
Teks Base 64 1/3 lebih panjang dari base 256. (2 ^ 8 vs 2 ^ 6; 8bits / 6bits = 1,333 rasio)
import("crypto/rand""encoding/base64""math")
func randomBase64String(l int)string{
buff := make([]byte,int(math.Round(float64(l)/float64(1.33333333333))))
rand.Read(buff)
str := base64.RawURLEncoding.EncodeToString(buff)return str[:l]// strip 1 extra character we get from odd length results}
Catatan: Anda juga dapat menggunakan RawStdEncoding jika Anda lebih suka + dan / karakter untuk - dan _
Jika Anda ingin hex, basis 16 adalah 2x lebih panjang dari basis 256. (2 ^ 8 vs 2 ^ 4; 8bits / 4bits = rasio 2x)
import("crypto/rand""encoding/hex""math")
func randomBase16String(l int)string{
buff := make([]byte,int(math.Round(float64(l)/2)))
rand.Read(buff)
str := hex.EncodeToString(buff)return str[:l]// strip 1 extra character we get from odd length results}
Namun, Anda bisa memperluas ini ke set karakter yang sewenang-wenang jika Anda memiliki encoder base256 ke baseN untuk set karakter Anda. Anda dapat melakukan perhitungan ukuran yang sama dengan berapa banyak bit yang dibutuhkan untuk mewakili set karakter Anda. Perhitungan rasio untuk rangkaian karakter acak adalah:ratio = 8 / log2(len(charset)) .
Meskipun kedua solusi ini aman, sederhana, harus cepat, dan jangan sia-siakan kolam entropi kripto Anda.
Layak disebutkan bahwa Go Playground selalu mengembalikan nomor acak yang sama, sehingga Anda tidak akan melihat di sana string acak yang berbeda di eksekusi yang berbeda dari kode itu
Ini cara saya) Gunakan matematika rand atau crypto rand sesuai keinginan.
func randStr(len int)string{
buff := make([]byte, len)
rand.Read(buff)
str := base64.StdEncoding.EncodeToString(buff)// Base 64 can be longer than lenreturn str[:len]}
Jika Anda ingin menambahkan beberapa karakter ke kumpulan karakter yang diizinkan, Anda dapat membuat kode berfungsi dengan apa saja yang menyediakan byte acak melalui io.Reader. Di sini kita gunakan crypto/rand.
// len(encodeURL) == 64. This allows (x <= 265) x % 64 to have an even// distribution.const encodeURL ="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_"// A helper function create and fill a slice of length n with characters from// a-zA-Z0-9_-. It panics if there are any problems getting random bytes.
func RandAsciiBytes(n int)[]byte{
output := make([]byte, n)// We will take n bytes, one byte for each character of output.
randomness := make([]byte, n)// read all random
_, err := rand.Read(randomness)if err !=nil{
panic(err)}// fill outputfor pos := range output {// get random item
random := uint8(randomness[pos])// random % 64
randomPos := random % uint8(len(encodeURL))// put into output
output[pos]= encodeURL[randomPos]}return output
}
Karena len(encodeURL) == 64. Jika random % 64tidak dilakukan, randomPosbisa> = 64 dan menyebabkan kepanikan di luar batas saat runtime.
0xcaff
-1
/*
korzhao
*/package rand
import(
crand "crypto/rand""math/rand""sync""time""unsafe")// Doesn't share the rand library globally, reducing lock contention
type Randstruct{Seed int64
Pool*sync.Pool}var(MRand=NewRand()
randlist =[]byte("abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890"))// init random number generator
func NewRand()*Rand{
p :=&sync.Pool{New: func()interface{}{return rand.New(rand.NewSource(getSeed()))},}
mrand :=&Rand{Pool: p,}return mrand
}// get the seed
func getSeed() int64 {return time.Now().UnixNano()}
func (s *Rand) getrand()*rand.Rand{return s.Pool.Get().(*rand.Rand)}
func (s *Rand) putrand(r *rand.Rand){
s.Pool.Put(r)}// get a random number
func (s *Rand)Intn(n int)int{
r := s.getrand()
defer s.putrand(r)return r.Intn(n)}// bulk get random numbers
func (s *Rand)Read(p []byte)(int, error){
r := s.getrand()
defer s.putrand(r)return r.Read(p)}
func CreateRandomString(len int)string{
b := make([]byte, len)
_, err :=MRand.Read(b)if err !=nil{return""}for i :=0; i < len; i++{
b[i]= randlist[b[i]%(62)]}return*(*string)(unsafe.Pointer(&b))}
Halo! Selamat datang di StackOverflow. Meskipun Anda menambahkan potongan kode, jawaban Anda tidak menyertakan konteks apa pun tentang "cara kerjanya" atau "mengapa hal ini dilakukan". Ingat juga pertanyaan ini diajukan dalam bahasa Inggris sehingga komentar Anda juga harus dalam bahasa Inggris.
Jawaban:
Solusi Paul menyediakan solusi sederhana dan umum.
Pertanyaannya menanyakan "cara tercepat dan termudah" . Mari kita membahas bagian tercepat juga. Kami akan tiba di kode final kami yang tercepat dengan cara berulang-ulang. Benchmarking setiap iterasi dapat ditemukan di akhir jawaban.
Semua solusi dan kode pembandingan dapat ditemukan di Go Playground . Kode di Playground adalah file tes, bukan yang dapat dieksekusi. Anda harus menyimpannya ke dalam file bernama
XX_test.go
dan menjalankannyaKata Pengantar :
Karena itu, bahkan jika Anda tidak membutuhkan solusi tercepat, membaca jawaban ini mungkin penuh petualangan dan mendidik.
I. Perbaikan
1. Genesis (Rune)
Sebagai pengingat, solusi umum asli yang kami tingkatkan adalah ini:
2. Bytes
Jika karakter untuk memilih dari dan merangkai string acak hanya berisi huruf besar dan kecil dari alfabet bahasa Inggris, kita dapat bekerja dengan byte hanya karena huruf alfabet Inggris memetakan ke byte 1-ke-1 dalam pengkodean UTF-8 (yang adalah cara Go menyimpan string).
Jadi alih-alih:
kita bisa menggunakan:
Atau lebih baik lagi:
Sekarang ini sudah merupakan perbaikan besar: kita bisa mencapainya menjadi
const
(adastring
konstanta tetapi tidak ada konstanta slice ). Sebagai keuntungan tambahan, ekspresilen(letters)
juga akan menjadiconst
! (Ekspresilen(s)
konstan jikas
string konstan.)Dan berapa biayanya? Tidak ada sama sekali.
string
s dapat diindeks yang mengindeks byte-nya, sempurna, persis seperti yang kita inginkan.Tujuan kami selanjutnya terlihat seperti ini:
3. Sisa
Solusi sebelumnya mendapatkan nomor acak untuk menunjuk surat acak dengan memanggil
rand.Intn()
delegasiRand.Intn()
mana yang akan didelegasikanRand.Int31n()
.Ini jauh lebih lambat dibandingkan dengan
rand.Int63()
yang menghasilkan angka acak dengan 63 bit acak.Jadi kita bisa memanggil
rand.Int63()
dan menggunakan sisanya setelah membaginya denganlen(letterBytes)
:Ini bekerja dan secara signifikan lebih cepat, kerugiannya adalah bahwa probabilitas semua huruf tidak akan persis sama (dengan asumsi
rand.Int63()
menghasilkan semua angka 63-bit dengan probabilitas yang sama). Meskipun distorsi sangat kecil karena jumlah huruf52
jauh lebih kecil daripada1<<63 - 1
, jadi dalam praktiknya ini baik-baik saja.Untuk membuat ini lebih mudah dipahami: katakanlah Anda ingin nomor acak dalam kisaran
0..5
. Menggunakan 3 bit acak, ini akan menghasilkan angka0..1
dengan probabilitas ganda daripada dari kisaran2..5
. Dengan menggunakan 5 bit acak, angka dalam kisaran0..1
akan muncul dengan6/32
probabilitas dan angka dalam kisaran2..5
dengan5/32
probabilitas yang sekarang lebih dekat ke yang diinginkan. Meningkatkan jumlah bit membuat ini kurang signifikan, ketika mencapai 63 bit, dapat diabaikan.4. Masking
Membangun pada solusi sebelumnya, kita dapat mempertahankan distribusi huruf yang sama dengan hanya menggunakan banyak bit terendah dari angka acak sebanyak yang diperlukan untuk mewakili jumlah huruf. Jadi misalnya jika kita memiliki 52 huruf, membutuhkan 6 bit untuk mewakilinya:
52 = 110100b
. Jadi kita hanya akan menggunakan 6 bit terendah dari angka yang dikembalikan olehrand.Int63()
. Dan untuk menjaga pemerataan surat, kami hanya "menerima" nomor itu jika berada dalam kisaran0..len(letterBytes)-1
. Jika bit terendah lebih besar, kami membuangnya dan meminta nomor acak baru.Perhatikan bahwa peluang bit terendah untuk lebih besar dari atau sama dengan
len(letterBytes)
kurang dari0.5
pada umumnya (0.25
rata-rata), yang berarti bahwa bahkan jika ini yang terjadi, mengulangi kasus "langka" ini mengurangi kemungkinan tidak menemukan barang yang baik. jumlah. Setelahn
pengulangan, kemungkinan kita masih tidak memiliki indeks yang baik adalah jauh lebih sedikit daripadapow(0.5, n)
, dan ini hanya perkiraan yang lebih tinggi. Dalam hal 52 huruf kemungkinan 6 bit terendah tidak bagus hanya(64-52)/64 = 0.19
; yang artinya misalnya peluang untuk tidak memiliki angka yang baik setelah 10 kali pengulangan1e-8
.Jadi, inilah solusinya:
5. Masking Ditingkatkan
Solusi sebelumnya hanya menggunakan 6 bit terendah dari 63 bit acak yang dikembalikan oleh
rand.Int63()
. Ini adalah pemborosan karena mendapatkan bit acak adalah bagian paling lambat dari algoritma kami.Jika kita memiliki 52 huruf, itu berarti 6 bit kode indeks huruf. Jadi 63 bit acak dapat menunjuk
63/6 = 10
indeks huruf yang berbeda. Mari kita gunakan semua 10 itu:6. Sumber
The Masking Peningkatan ini cukup bagus, tidak banyak yang bisa kita memperbaiki itu. Kita bisa, tetapi tidak sebanding dengan kerumitannya.
Sekarang mari kita cari hal lain untuk diperbaiki. Sumber angka acak.
Ada
crypto/rand
paket yang menyediakanRead(b []byte)
fungsi, jadi kita bisa menggunakannya untuk mendapatkan banyak byte dengan satu panggilan sebanyak yang kita butuhkan. Ini tidak akan membantu dalam hal kinerja karenacrypto/rand
mengimplementasikan generator nomor pseudorandom yang aman secara kriptografis sehingga jauh lebih lambat.Jadi mari kita tetap berpegang pada
math/rand
paket. Therand.Rand
menggunakanrand.Source
sebagai sumber bit acak.rand.Source
adalah antarmuka yang menentukanInt63() int64
metode: tepat dan satu-satunya hal yang kami butuhkan dan gunakan dalam solusi terbaru kami.Jadi kita tidak benar-benar membutuhkan
rand.Rand
(baik eksplisit atau global, berbagi salah saturand
paket), arand.Source
sudah cukup sempurna bagi kita:Juga catatan bahwa solusi terakhir ini tidak mengharuskan Anda untuk menginisialisasi (benih) global
Rand
darimath/rand
paket sebagai yang tidak digunakan (dan kamirand.Source
benar diinisialisasi / unggulan).Satu hal lagi yang perlu diperhatikan di sini: paket dokumen
math/rand
negara:Jadi sumber default lebih lambat dari
Source
yang dapat diperolehrand.NewSource()
, karena sumber default harus memberikan keamanan di bawah akses / penggunaan bersamaan, sementararand.NewSource()
tidak menawarkan ini (dan dengan demikianSource
dikembalikan oleh lebih mungkin lebih cepat).7. Memanfaatkan
strings.Builder
Semua solusi sebelumnya menghasilkan
string
konten yang pertama kali dibuat dalam slice ([]rune
dalam Genesis , dan[]byte
dalam solusi berikutnya), dan kemudian dikonversi menjadistring
. Konversi akhir ini harus membuat salinan konten slice, karenastring
nilainya tidak berubah, dan jika konversi tidak akan membuat salinan, tidak dapat dijamin bahwa konten string tidak dimodifikasi melalui slice aslinya. Untuk detailnya, lihat Bagaimana mengonversi utf8 string ke [] byte? dan golang: [] byte (string) vs [] byte (* string) .Go 1.10 diperkenalkan
strings.Builder
.strings.Builder
tipe baru yang bisa kita gunakan untuk membangun konten yangstring
mirip denganbytes.Buffer
. Itu melakukannya secara internal menggunakan[]byte
, dan ketika kita selesai, kita bisa mendapatkan nilai akhirstring
menggunakan ituBuilder.String()
metodenya. Tapi apa yang keren di dalamnya adalah ia melakukan ini tanpa melakukan salinan yang baru saja kita bicarakan di atas. Ia berani melakukannya karena irisan byte yang digunakan untuk membangun konten string tidak diekspos, sehingga dijamin tidak ada yang dapat memodifikasinya secara tidak sengaja atau jahat untuk mengubah string "immutable" yang dihasilkan.Jadi ide kami berikutnya adalah untuk tidak membangun string acak dalam sebuah irisan, tetapi dengan bantuan a
strings.Builder
, jadi setelah kami selesai, kami dapat memperoleh dan mengembalikan hasilnya tanpa harus membuat salinannya. Ini mungkin membantu dalam hal kecepatan, dan pasti akan membantu dalam hal penggunaan dan alokasi memori.Perhatikan bahwa setelah membuat yang baru
strings.Buidler
, kami memanggilBuilder.Grow()
metodenya, memastikannya mengalokasikan irisan internal yang cukup besar (untuk menghindari realokasi saat kami menambahkan huruf acak).8. "Meniru"
strings.Builder
dengan paketunsafe
strings.Builder
membangun string di internal[]byte
, sama seperti yang kita lakukan sendiri. Jadi pada dasarnya melakukannya melaluistrings.Builder
beberapa overhead, satu-satunya hal yang kita beralihstrings.Builder
adalah untuk menghindari penyalinan akhir dari slice.strings.Builder
menghindari salinan akhir dengan menggunakan paketunsafe
:Masalahnya, kita juga bisa melakukan ini sendiri. Jadi idenya di sini adalah untuk kembali ke membangun string acak dalam
[]byte
, tetapi ketika kita selesai, jangan mengubahnyastring
untuk kembali, tetapi lakukan konversi yang tidak aman: dapatkanstring
yang menunjuk ke byte slice kami sebagai data string .Ini adalah bagaimana hal itu dapat dilakukan:
(9. Menggunakan
rand.Read()
)Go 1.7 menambahkan suatu
rand.Read()
fungsi danRand.Read()
metode. Kita harus tergoda untuk menggunakan ini untuk membaca byte sebanyak yang kita butuhkan dalam satu langkah, untuk mencapai kinerja yang lebih baik.Ada satu "masalah" kecil dengan ini: berapa banyak byte yang kita butuhkan? Kita bisa mengatakan: sebanyak jumlah surat keluaran. Kami akan berpikir ini adalah estimasi atas, karena indeks huruf menggunakan kurang dari 8 bit (1 byte). Tetapi pada titik ini kita sudah melakukan yang lebih buruk (karena mendapatkan bit acak adalah "bagian yang sulit"), dan kita mendapatkan lebih dari yang dibutuhkan.
Juga perhatikan bahwa untuk menjaga distribusi yang sama dari semua indeks huruf, mungkin ada beberapa data acak "sampah" yang tidak dapat kami gunakan, jadi kami akan melewatkan beberapa data, dan dengan demikian berakhir pendek ketika kami memeriksa semua potongan byte. Kita perlu mendapatkan lebih banyak byte acak, "secara rekursif". Dan sekarang kita bahkan kehilangan keuntungan "satu panggilan ke
rand
paket" ...Kami dapat "agak" mengoptimalkan penggunaan data acak yang kami peroleh
math.Rand()
. Kami dapat memperkirakan berapa banyak byte (bit) yang kami butuhkan. 1 huruf membutuhkanletterIdxBits
bit, dan kita perlun
huruf, jadi kita perlun * letterIdxBits / 8.0
byte yang mengumpulkan. Kami dapat menghitung probabilitas indeks acak tidak dapat digunakan (lihat di atas), jadi kami dapat meminta lebih banyak yang "lebih mungkin" cukup (jika ternyata tidak, kami ulangi prosesnya). Kita dapat memproses byte slice sebagai "bit stream" misalnya, yang kami punya lib pihak ke-3 yang bagus:github.com/icza/bitio
(pengungkapan: Saya penulisnya).Namun kode Benchmark masih menunjukkan kita tidak menang. Kenapa gitu?
Jawaban untuk pertanyaan terakhir adalah karena
rand.Read()
menggunakan perulangan dan terus memanggilSource.Int63()
hingga mengisi irisan yang dilewati. Apa yang dilakukanRandStringBytesMaskImprSrc()
solusinya, tanpa buffer perantara, dan tanpa kompleksitas tambahan. Itu sebabnyaRandStringBytesMaskImprSrc()
tetap di atas takhta. Ya,RandStringBytesMaskImprSrc()
gunakan yang tidak disinkronkanrand.Source
sepertirand.Read()
. Namun alasannya masih berlaku; dan yang terbukti jika kita menggunakanRand.Read()
bukanrand.Read()
(yang sebelumnya juga tidak disinkronkan).II Tolok ukur
Baiklah, sudah waktunya untuk membandingkan solusi yang berbeda.
Momen kebenaran:
Hanya dengan beralih dari rune ke byte, kami langsung memiliki peningkatan kinerja 24% , dan kebutuhan memori turun menjadi sepertiga .
Menyingkirkan
rand.Intn()
dan menggunakanrand.Int63()
sebagai gantinya memberikan 20% lagi .Masking (dan mengulangi jika indeks besar) sedikit melambat (karena panggilan pengulangan): -22% ...
Tetapi ketika kita menggunakan semua (atau sebagian besar) dari 63 bit acak (10 indeks dari satu
rand.Int63()
panggilan): yang mempercepat waktu besar: 3 kali .Jika kita puas dengan (non-default, baru)
rand.Source
bukanrand.Rand
, kita lagi memperoleh 21%.Jika kita menggunakan
strings.Builder
, kita memperoleh kecil 3,5% di kecepatan , tapi kami juga mencapai 50% pengurangan penggunaan memori dan alokasi! Itu bagus!Akhirnya jika kita berani menggunakan paket,
unsafe
bukanstrings.Builder
, kita lagi mendapatkan 14% yang bagus .Membandingkan final dengan solusi awal:
RandStringBytesMaskImprSrcUnsafe()
adalah 6,3 kali lebih cepat daripadaRandStringRunes()
, menggunakan memori keenam dan alokasi setengah . Misi selesai.sumber
rand.Source
digunakan bersama . Solusi yang lebih baik adalah dengan meneruskanrand.Source
keRandStringBytesMaskImprSrc()
fungsi, dan dengan demikian tidak diperlukan penguncian dan karenanya kinerja / efisiensi tidak terpengaruh. Setiap goroutine bisa memiliki sendiriSource
.defer
ketika jelas bahwa Anda tidak membutuhkannya. Lihat grokbase.com/t/gg/golang-nuts/158zz5p42w/…defer
untuk membuka kunci mutex baik segera sebelum atau setelah memanggil kunci adalah IMO sebagian besar adalah ide yang sangat bagus; Anda dijamin untuk keduanya tidak lupa untuk membuka kunci tetapi juga membuka kunci meskipun dalam fungsi panik yang tidak fatal.Anda bisa menulis kode untuk itu. Kode ini bisa sedikit lebih sederhana jika Anda ingin mengandalkan huruf-huruf yang semuanya byte tunggal ketika dikodekan dalam UTF-8.
sumber
rand.Seed(time.Now().Unix())
ataurand.Seed(time.Now().UnixNano())
math/rand
; gunakancrypto/rand
(seperti opsi @ Not_A_Golfer 1).Gunakan paket uniuri , yang menghasilkan string seragam (tidak bias) yang aman secara kriptografis.
Penafian: Saya pembuat paket
sumber
Dua opsi yang mungkin (tentu saja ada lebih banyak):
Anda dapat menggunakan
crypto/rand
paket yang mendukung membaca array byte acak (dari / dev / urandom) dan diarahkan untuk pembuatan acak kriptografis. lihat http://golang.org/pkg/crypto/rand/#example_Read . Mungkin lebih lambat dari generasi nomor pseudo-acak normal.Ambil nomor acak dan hash menggunakan md5 atau sesuatu seperti ini.
sumber
Mengikuti
icza's
solusi yang dijelaskan dengan luar biasa, berikut ini adalah modifikasi yang menggunakancrypto/rand
alih-alihmath/rand
.Jika Anda menginginkan solusi yang lebih umum, yang memungkinkan Anda meneruskan potongan byte karakter untuk membuat string keluar, Anda dapat mencoba menggunakan ini:
Jika Anda ingin meneruskan sumber keacakan Anda sendiri, akan sepele untuk memodifikasi di atas untuk menerima
io.Reader
alih - alih menggunakancrypto/rand
.sumber
jika kamu mau angka acak yang aman secara kriptografis , dan rangkaian karakter yang tepat fleksibel (misalnya, base64 baik-baik saja), Anda dapat menghitung dengan tepat berapa panjang karakter acak yang Anda butuhkan dari ukuran output yang diinginkan.
Teks Base 64 1/3 lebih panjang dari base 256. (2 ^ 8 vs 2 ^ 6; 8bits / 6bits = 1,333 rasio)
Catatan: Anda juga dapat menggunakan RawStdEncoding jika Anda lebih suka + dan / karakter untuk - dan _
Jika Anda ingin hex, basis 16 adalah 2x lebih panjang dari basis 256. (2 ^ 8 vs 2 ^ 4; 8bits / 4bits = rasio 2x)
Namun, Anda bisa memperluas ini ke set karakter yang sewenang-wenang jika Anda memiliki encoder base256 ke baseN untuk set karakter Anda. Anda dapat melakukan perhitungan ukuran yang sama dengan berapa banyak bit yang dibutuhkan untuk mewakili set karakter Anda. Perhitungan rasio untuk rangkaian karakter acak adalah:
ratio = 8 / log2(len(charset))
.Meskipun kedua solusi ini aman, sederhana, harus cepat, dan jangan sia-siakan kolam entropi kripto Anda.
Inilah taman bermain yang menunjukkan bahwa itu berfungsi untuk berbagai ukuran. https://play.golang.org/p/i61WUVR8_3Z
sumber
sumber
[]byte
?Ini cara saya) Gunakan matematika rand atau crypto rand sesuai keinginan.
sumber
Jika Anda ingin menambahkan beberapa karakter ke kumpulan karakter yang diizinkan, Anda dapat membuat kode berfungsi dengan apa saja yang menyediakan byte acak melalui io.Reader. Di sini kita gunakan
crypto/rand
.sumber
random % 64
perlu?len(encodeURL) == 64
. Jikarandom % 64
tidak dilakukan,randomPos
bisa> = 64 dan menyebabkan kepanikan di luar batas saat runtime.24.0 ns / op 16 B / op 1 allocs /
sumber
BenchmarkRandStr16-8 20000000 68.1 ns / op 16 B / op 1 allocs / op
sumber