আমি যেতে চাই কেবলমাত্র অক্ষরের একটি এলোমেলো স্ট্রিং (বড় হাতের বা ছোট হাতের অক্ষর), কোনও সংখ্যা নেই Go এটি করার দ্রুত এবং সহজ উপায় কী?
আমি যেতে চাই কেবলমাত্র অক্ষরের একটি এলোমেলো স্ট্রিং (বড় হাতের বা ছোট হাতের অক্ষর), কোনও সংখ্যা নেই Go এটি করার দ্রুত এবং সহজ উপায় কী?
উত্তর:
পলের সমাধানটি একটি সাধারণ , সাধারণ সমাধান সরবরাহ করে।
প্রশ্নটি "দ্রুততম এবং সহজতম উপায়" জন্য জিজ্ঞাসা করে । আসুন দ্রুততম অংশটিও সম্বোধন করি। আমরা আমাদের চূড়ান্ত, দ্রুততম কোডটি পুনরাবৃত্ত পদ্ধতিতে পৌঁছে যাব। প্রতিটি পুনরাবৃত্তির মানদণ্ড উত্তরের শেষে পাওয়া যাবে।
সমস্ত সমাধান এবং বেঞ্চমার্কিং কোড গো প্লেগ্রাউন্ডে পাওয়া যাবে । প্লেগ্রাউন্ডের কোডটি একটি পরীক্ষামূলক ফাইল, কোনও এক্সিকিউটেবল নয়। আপনাকে এটি নামের একটি ফাইলে সংরক্ষণ করতে হবে এবং XX_test.go
এটি দিয়ে চালাতে হবে
go test -bench . -benchmem
মূল শব্দ :
আপনার কেবল একটি এলোমেলো স্ট্রিং প্রয়োজন হলে দ্রুততম সমাধানটি গ-টু সমাধান নয়। তার জন্য, পলের সমাধান নিখুঁত। এটি যদি পারফরম্যান্সে গুরুত্বপূর্ণ হয়। যদিও প্রথম 2 টি পদক্ষেপ ( বাইটস এবং রিমাইন্ডার ) একটি গ্রহণযোগ্য সমঝোতা হতে পারে: তারা 50% এর মত পারফরম্যান্সকে উন্নত করে ( দ্বিতীয়টিতে সঠিক সংখ্যা দেখুন Ben বেঞ্চমার্ক বিভাগে), এবং তারা জটিলতা উল্লেখযোগ্যভাবে বাড়ায় না।
এটি বলার পরেও, আপনার যদি দ্রুততম সমাধানের প্রয়োজন না হয়, এই উত্তরের মাধ্যমে পড়া পড়া সাহসিক এবং শিক্ষামূলক হতে পারে।
অনুস্মারক হিসাবে, আসল, সাধারণ সমাধান যা আমরা উন্নতি করছি তা হ'ল:
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))]
}
return string(b)
}
অক্ষরটি চয়ন করতে এবং এলোমেলোভাবে বর্ণমালা স্ট্রিংয়ে ইংরেজি বর্ণমালার কেবলমাত্র বড় হাতের এবং ছোট হাতের অক্ষর থাকে তবে আমরা কেবল বাইট দিয়ে কাজ করতে পারি কারণ ইংলিশ বর্ণমালা অক্ষর ইউটিএফ -8 এনকোডিংয়ের 1-থেকে -1 বাইটে ম্যাপ করে (যা কীভাবে গো স্ট্রিং স্টোর করে)।
এর পরিবর্তে:
var letters = []rune("abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ")
আমরা ব্যবহার করতে পারি:
var letters = []bytes("abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ")
বা আরও ভাল:
const letters = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"
এখন এটি ইতিমধ্যে একটি বড় উন্নতি: আমরা এটি হওয়ার জন্য এটি অর্জন করতে পারি const
(সেখানে string
ধ্রুবক রয়েছে তবে কোনও স্লাইস ধ্রুবক নেই )। অতিরিক্ত লাভ হিসাবে, এক্সপ্রেশনটিও len(letters)
একটি হবে const
! ( স্ট্রিং ধ্রুবক len(s)
হলে অভিব্যক্তিটি স্থির থাকে s
))
আর কী দামে? কিছু না. string
গুলিকে সূচকযুক্ত করা যেতে পারে যা এর বাইটগুলি সূচক করে, নিখুঁত, ঠিক আমরা কী চাই।
আমাদের পরবর্তী গন্তব্য এটির মতো দেখাচ্ছে:
const letterBytes = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"
func RandStringBytes(n int) string {
b := make([]byte, n)
for i := range b {
b[i] = letterBytes[rand.Intn(len(letterBytes))]
}
return string(b)
}
পূর্ববর্তী সমাধানগুলি rand.Intn()
কোন প্রতিনিধি কোন প্রতিনিধিকে কল করে কল করে একটি এলোমেলো চিঠি নির্ধারণ করতে একটি এলোমেলো নম্বর Rand.Intn()
পান Rand.Int31n()
।
এটি তুলনায় অনেক ধীরগতির rand.Int63()
সাথে 63 র্যান্ডম বিট সহ একটি এলোমেলো সংখ্যা তৈরি করে।
সুতরাং আমরা সহজেই কল করে rand.Int63()
ভাগ করে নেওয়ার পরে বাকীটি ব্যবহার করতে পারি len(letterBytes)
:
func RandStringBytesRmndr(n int) string {
b := make([]byte, n)
for i := range b {
b[i] = letterBytes[rand.Int63() % int64(len(letterBytes))]
}
return string(b)
}
এটি কাজ করে এবং উল্লেখযোগ্যভাবে দ্রুততর হয়, অসুবিধাটি হ'ল সমস্ত বর্ণগুলির সম্ভাবনা হুবহু এক রকম হবে না (ধরে নেওয়া rand.Int63()
সমান সম্ভাবনা সহ সমস্ত 63-বিট সংখ্যা উত্পন্ন করে)। বর্ণের সংখ্যা 52
তুলনায় অনেক বেশি হ'ল যদিও বিকৃতিটি অত্যন্ত ছোট 1<<63 - 1
, সুতরাং বাস্তবে এটি পুরোপুরি ঠিক।
এটি বুঝতে সহজতর করার জন্য: আসুন আমরা বলি যে আপনি এর পরিসীমাতে একটি এলোমেলো নম্বর চান 0..5
। ৩ টি এলোমেলো বিট ব্যবহার করে, 0..1
এটি পরিসরের চেয়ে দ্বিগুণ সম্ভাবনা সহ সংখ্যাগুলি তৈরি করবে 2..5
। 5 র্যান্ডম বিট ব্যবহার করে, সীমার মধ্যে সংখ্যার 0..1
সঙ্গে ঘটবে 6/32
সম্ভাবনা ও সীমার মধ্যে সংখ্যার 2..5
সঙ্গে 5/32
সম্ভাব্যতা যা এখন পছন্দসই কাছাকাছি। বিটের সংখ্যা বাড়ানো এটিকে কম তাৎপর্যযুক্ত করে তোলে, যখন 63 বিটে পৌঁছায়, তা নগন্য নয়।
পূর্ববর্তী সমাধানের উপর ভিত্তি করে, আমরা কেবলমাত্র এলোমেলো সংখ্যার ন্যূনতম বিটগুলির মধ্যে যতগুলি অক্ষরের সংখ্যা উপস্থাপন করতে হয় প্রয়োজনীয় হিসাবে অক্ষরের সমান বন্টন বজায় রাখতে পারি। সুতরাং উদাহরণস্বরূপ, যদি আমরা 52 অক্ষর আছে, 6 বিট প্রয়োজন এটা প্রতিনিধিত্ব করতে: 52 = 110100b
। সুতরাং আমরা কেবল ফিরে আসা সংখ্যার সর্বনিম্ন 6 বিট ব্যবহার করব rand.Int63()
। এবং অক্ষরের সমান বন্টন বজায় রাখার জন্য, আমরা কেবলমাত্র সংখ্যাটি যদি "রেঞ্জ" এ পড়ে তবে কেবল "গ্রহণ করি" 0..len(letterBytes)-1
। যদি সর্বনিম্ন বিটগুলি বেশি হয়, আমরা এটিকে বাতিল করব এবং একটি নতুন এলোমেলো সংখ্যাটি জিজ্ঞাসা করব।
নোট করুন যে সর্বনিম্ন বিটগুলি তার চেয়ে বড় বা সমান হওয়ার len(letterBytes)
সম্ভাবনাটি 0.5
সাধারণের তুলনায় কম ( 0.25
গড়), যার অর্থ এটি যদি এমন হয় তবে এই "বিরল" ক্ষেত্রে পুনরাবৃত্তি করা ভাল খুঁজে না পাওয়ার সম্ভাবনা হ্রাস করে সংখ্যা। n
পুনরাবৃত্তির পরে , আমাদের ভাল সূচক না হওয়ার সম্ভাবনাটি এর চেয়ে অনেক কম pow(0.5, n)
এবং এটি কেবল একটি উচ্চতর অনুমান। 52 টি অক্ষরের ক্ষেত্রে 6 টি সর্বনিম্ন বিটগুলি ভাল না হওয়ার সুযোগটি কেবলমাত্র (64-52)/64 = 0.19
; যার অর্থ উদাহরণস্বরূপ যে 10 পুনরাবৃত্তি হওয়ার পরে ভাল নম্বর না হওয়ার সম্ভাবনা রয়েছে 1e-8
।
সুতরাং সমাধান এখানে:
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++
}
}
return string(b)
}
পূর্ববর্তী সমাধানটি কেবলমাত্র 63 টি এলোমেলো বিটগুলির মধ্যে সর্বনিম্ন 6 বিট ব্যবহার করে rand.Int63()
। এলোমেলো বিট পাওয়া আমাদের অ্যালগরিদমের সবচেয়ে ধীর অংশ এটি এটি একটি অপচয়।
যদি আমাদের 52 টি অক্ষর থাকে তবে এর অর্থ 6 টি বিট কোড একটি চিঠি সূচক। সুতরাং rand৩ টি এলোমেলো বিট 63/6 = 10
বিভিন্ন বর্ণ সূচি নির্ধারণ করতে পারে । আসুন এই সমস্ত 10 ব্যবহার করুন:
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--
}
return string(b)
}
কাচ উন্নত , বেশ ভাল অনেক আমরা এটি উন্নত করতে পারেন। আমরা পারতাম, তবে জটিলতার জন্য মূল্যবান নয়।
এবার উন্নতি করার জন্য আরও কিছু খুঁজে বের করা যাক। এলোমেলো সংখ্যার উত্স।
এখানে একটি crypto/rand
প্যাকেজ রয়েছে যা একটি Read(b []byte)
ফাংশন সরবরাহ করে, তাই আমরা আমাদের প্রয়োজন এমন একক কল সহ যতগুলি বাইট পেতে পারি তা ব্যবহার করতে পারি। এটি crypto/rand
কোনও ক্রিপ্টোগ্রাফিক সুরক্ষিত সিউডোর্যান্ডম নম্বর জেনারেটর প্রয়োগ করে তাই পারফরম্যান্সের ক্ষেত্রে সহায়তা করতে পারে না তাই এটি অনেক ধীর।
সুতরাং math/rand
প্যাকেজ আটকে রাখা যাক । rand.Rand
একটি ব্যবহার rand.Source
র্যান্ডম বিট উৎস হিসেবে। rand.Source
একটি ইন্টারফেস যা একটি Int63() int64
পদ্ধতি নির্দিষ্ট করে : হ'ল এবং আমাদের সর্বশেষ সমাধানে কেবল আমাদের প্রয়োজন এবং ব্যবহৃত।
সুতরাং আমাদের সত্যই rand.Rand
কোনও প্রয়োজন নেই (হয় স্পষ্ট বা বিশ্বব্যাপী, rand
প্যাকেজের মধ্যে একটি ভাগ করে নেওয়া ), 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 < len(letterBytes) {
b[i] = letterBytes[idx]
i--
}
cache >>= letterIdxBits
remain--
}
return string(b)
}
এছাড়াও মনে রাখবেন এই শেষ সমাধান (বীজ) আরম্ভ করতে বিশ্বব্যাপী আপনি প্রয়োজন হয় না Rand
এর math/rand
প্যাকেজ হিসাবে যে ব্যবহার করা হয় না (এবং আমাদের rand.Source
সঠিকভাবে সক্রিয়া / বীজযুক্ত হয় না)।
এখানে আরও একটি বিষয় লক্ষণীয়: প্যাকেজ ডক অফ math/rand
স্টেটস:
ডিফল্ট উত্স একাধিক গোরোটাইন দ্বারা সমবর্তী ব্যবহারের জন্য নিরাপদ।
সুতরাং ডিফল্ট উত্সটি Source
যেটি পেতে পারে তার চেয়ে ধীরে ধীরে rand.NewSource()
, কারণ ডিফল্ট উত্সটি সমবর্তী অ্যাক্সেস / ব্যবহারের অধীনে সুরক্ষা সরবরাহ করতে হবে, যখন rand.NewSource()
এটি অফার করে না (এবং এইভাবে এটি Source
দ্বারা ফিরে আসা দ্রুততর হওয়ার সম্ভাবনা বেশি থাকে)।
strings.Builder
আগের সমস্ত সমাধান আসতে একটি string
যাদের সামগ্রী প্রথম একটি ফালি সালে নির্মিত হয় ( []rune
মধ্যে আদিপুস্তক , এবং []byte
পরবর্তী সমাধান), এবং তারপর রূপান্তরিত string
। এই চূড়ান্ত রূপান্তরটি স্লাইসের সামগ্রীটির একটি অনুলিপি তৈরি করতে হবে, কারণ string
মানগুলি অপরিবর্তনীয়, এবং যদি রূপান্তরটি একটি অনুলিপি তৈরি করে না, তবে নিশ্চয়তা দেওয়া যায় না যে স্ট্রিংয়ের বিষয়বস্তুটি তার মূল টুকরো দিয়ে পরিবর্তিত হয়নি। বিশদগুলির জন্য, কীভাবে utf8 স্ট্রিং [] বাইটে রূপান্তর করবেন? এবং গোলং: [] বাইট (স্ট্রিং) বনাম [] বাইট (* স্ট্রিং) ।
1.10 চালু করা হয়েছে strings.Builder
। strings.Builder
একটি নতুন ধরণের আমরা এর string
অনুরূপ সামগ্রী তৈরি করতে ব্যবহার করতে পারি bytes.Buffer
। এটি অভ্যন্তরীণভাবে এটি ব্যবহার করে []byte
এবং যখন আমাদের কাজ শেষ হয়, আমরা string
এর Builder.String()
পদ্ধতিটি ব্যবহার করে চূড়ান্ত মান অর্জন করতে পারি । তবে এতে শীতল হ'ল এটি হ'ল উপরে বর্ণিত অনুলিপিটি সম্পাদন না করেই এটি করে। এটি এমনটা করার সাহস করে কারণ স্ট্রিংয়ের সামগ্রী তৈরি করতে ব্যবহৃত বাইট স্লাইসটি প্রকাশিত হয় না, তাই গ্যারান্টিযুক্ত যে উত্পাদিত "অপরিবর্তনীয়" স্ট্রিংটিকে পরিবর্তন করতে কেউ অনিচ্ছাকৃতভাবে বা দূষিতভাবে এটিকে সংশোধন করতে পারে না।
সুতরাং আমাদের পরবর্তী ধারণাটি হ'ল এক স্লাইজে এলোমেলো স্ট্রিংটি তৈরি করা নয়, তবে একটির সাহায্যে strings.Builder
, সুতরাং একবার কাজটি শেষ হয়ে গেলে, আমরা এর অনুলিপি তৈরি না করেই ফলাফলটি পেতে এবং ফিরে আসতে পারি। এটি গতির ক্ষেত্রে সহায়তা করতে পারে এবং এটি মেমরির ব্যবহার এবং বরাদ্দের ক্ষেত্রে অবশ্যই সহায়তা করবে।
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()
}
মনে রাখবেন যে একটি নতুন তৈরি করার পরে strings.Buidler
, আমরা এর Builder.Grow()
পদ্ধতিটি কল করেছি, এটি নিশ্চিত করে যে এটি একটি যথেষ্ট পরিমাণে অভ্যন্তরীণ স্লাইস বরাদ্দ করেছে (আমরা এলোমেলো অক্ষরগুলি যুক্ত করার সাথে সাথে পুনঃনির্মাণগুলি এড়াতে)।
strings.Builder
প্যাকেজ সহ "নকল"unsafe
strings.Builder
একটি অভ্যন্তরীণে স্ট্রিং তৈরি করে []byte
, যেমন আমরা নিজেরাই করেছিলাম। সুতরাং মূলত এটির মাধ্যমে strings.Builder
এটির কিছুটা ওভারহেড থাকে, কেবলমাত্র আমরা স্যুইচ করেছিলাম strings.Builder
স্লাইসের চূড়ান্ত অনুলিপি এড়ানো।
strings.Builder
প্যাকেজ ব্যবহার করে চূড়ান্ত অনুলিপি এড়ানো unsafe
:
// String returns the accumulated string.
func (b *Builder) String() string {
return *(*string)(unsafe.Pointer(&b.buf))
}
বিষয়টি হ'ল আমরা নিজেও এটি করতে পারি। সুতরাং এখানে ধারণাটি একটি এ এলোমেলো স্ট্রিং তৈরির দিকে ফিরে যেতে হবে []byte
, কিন্তু যখন আমরা শেষ হয়ে যাই তখন এটিকে পুনরায় রূপান্তর করবেন না string
, তবে একটি অনিরাপদ রূপান্তর করুন: string
স্ট্রিং ডেটা হিসাবে আমাদের বাইট টুকরোটির দিকে নির্দেশিত কোনটি পান ।
এটি এটি করা যেতে পারে:
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))
}
rand.Read()
)Go 1.7 একটি rand.Read()
ফাংশন এবং একটি Rand.Read()
পদ্ধতি যুক্ত করেছে। আমাদের আরও ভাল পারফরম্যান্স অর্জনের জন্য এক ধাপে আমাদের যতটুকু বাইট প্রয়োজন তা পড়তে প্ররোচিত করতে হবে।
এটির সাথে একটি ছোট "সমস্যা" রয়েছে: আমাদের কত বাইট দরকার? আমরা বলতে পারি: যতগুলি আউটপুট বর্ণের সংখ্যা। আমরা মনে করব এটি একটি উচ্চতর অনুমান, কারণ একটি চিঠি সূচক 8 বিটের (1 বাইট) কম ব্যবহার করে। তবে এই মুহুর্তে আমরা ইতিমধ্যে আরও খারাপ করছি (যেমনটি এলোমেলো বিটগুলি পাওয়া "শক্ত অংশ"), এবং আমরা প্রয়োজনের তুলনায় আরও বেশি পাচ্ছি।
এছাড়াও নোট করুন যে সমস্ত অক্ষরের সূচকের সমান বন্টন বজায় রাখার জন্য, কিছু "আবর্জনা" র্যান্ডম ডেটা থাকতে পারে যা আমরা ব্যবহার করতে পারব না, তাই আমরা কিছু ডেটা এড়িয়ে শেষ করব এবং এইভাবে সংক্ষিপ্তটি শেষ করে যখন আমরা সমস্ত কিছু যাব বাইট স্লাইস আমাদের আরও "এলোমেলোভাবে" আরও এলোমেলো বাইট পেতে হবে। এবং এখন আমরা " rand
প্যাকেজের একক কল " সুবিধা হারাচ্ছি ...
আমরা যে জাতীয় থেকে এলোমেলো তথ্য অর্জন করেছি তার ব্যবহারকে "কিছুটা" অপ্টিমাইজ করতে পারি math.Rand()
। আমরা অনুমান করতে পারি আমাদের কতগুলি বাইট (বিট) লাগবে। 1 টি বর্ণের জন্য letterIdxBits
বিট প্রয়োজন , এবং আমাদের n
চিঠিগুলি দরকার , সুতরাং আমাদের n * letterIdxBits / 8.0
বাইটগুলি বৃত্তাকার দরকার । আমরা এলোমেলো সূচকের ব্যবহারযোগ্য না হওয়ার সম্ভাবনাটি গণনা করতে পারি (উপরে দেখুন), তাই আমরা আরও বেশি অনুরোধ করতে পারি যা "আরও সম্ভবত" যথেষ্ট হবে (যদি এটি সক্রিয় হয় তবে এটি প্রক্রিয়াটি পুনরাবৃত্তি করবে)। আমরা উদাহরণস্বরূপ "বিট স্ট্রিম" হিসাবে বাইট স্লাইসটি প্রক্রিয়া করতে পারি, যার জন্য আমাদের একটি দুর্দান্ত তৃতীয় পক্ষের lib রয়েছে: github.com/icza/bitio
(প্রকাশ: আমি লেখক)।
তবে বেঞ্চমার্ক কোড এখনও দেখায় যে আমরা জিততে পারি না। এটা এমন কেন?
শেষ প্রশ্নের উত্তর হ'ল কারণ rand.Read()
একটি লুপ ব্যবহার Source.Int63()
করে এবং পাস করা টুকরোটি পূরণ না করা পর্যন্ত কল করতে থাকে। ঠিক কি RandStringBytesMaskImprSrc()
সমাধান করে, ছাড়া অন্তর্বর্তী বাফার, এবং যোগ জটিলতা ছাড়াই। সেকারণে RandStringBytesMaskImprSrc()
সিংহাসনে রয়েছেন। হ্যাঁ, RandStringBytesMaskImprSrc()
অসম্পূর্ণ rand.Source
বিপরীতে ব্যবহার করে rand.Read()
। তবে যুক্তি এখনও প্রযোজ্য; এবং এটি প্রমাণিত যদি আমরা এর Rand.Read()
পরিবর্তে ব্যবহার করি rand.Read()
(পূর্ববর্তীটিও অবিচ্ছিন্ন)।
ঠিক আছে, বিভিন্ন সমাধানের বেঞ্চমার্কিংয়ের সময় এসেছে।
সত্য মুহূর্ত:
BenchmarkRunes-4 2000000 723 ns/op 96 B/op 2 allocs/op
BenchmarkBytes-4 3000000 550 ns/op 32 B/op 2 allocs/op
BenchmarkBytesRmndr-4 3000000 438 ns/op 32 B/op 2 allocs/op
BenchmarkBytesMask-4 3000000 534 ns/op 32 B/op 2 allocs/op
BenchmarkBytesMaskImpr-4 10000000 176 ns/op 32 B/op 2 allocs/op
BenchmarkBytesMaskImprSrc-4 10000000 139 ns/op 32 B/op 2 allocs/op
BenchmarkBytesMaskImprSrcSB-4 10000000 134 ns/op 16 B/op 1 allocs/op
BenchmarkBytesMaskImprSrcUnsafe-4 10000000 115 ns/op 16 B/op 1 allocs/op
কেবল রুনস থেকে বাইটে স্যুইচ করার মাধ্যমে, আমরা সঙ্গে সঙ্গে 24% পারফরম্যান্স লাভ করেছি এবং মেমরির প্রয়োজনীয়তা এক তৃতীয়াংশে নেমে যায় ।
এর থেকে মুক্তি rand.Intn()
এবং এর rand.Int63()
পরিবর্তে আরও 20% জোর দেয় 20
মাস্কিং (এবং বড় সূচকগুলির ক্ষেত্রে পুনরাবৃত্তি) কিছুটা কমিয়ে দেয় (পুনরাবৃত্তি কলগুলির কারণে): -22% ...
কিন্তু যখন আমরা rand৩ টি এলোমেলো বিটগুলির মধ্যে সমস্ত (বা বেশিরভাগ) ব্যবহার করি (একটি rand.Int63()
কল থেকে 10 সূচক ): এটি বড় সময় গতিবেগ করে: 3 বার ।
আমরা যদি (অ-ডিফল্ট, নতুন) সঙ্গে নিষ্পত্তির rand.Source
পরিবর্তে rand.Rand
, আমরা আবার লাভ 21%।
আমরা যদি কাজে লাগাতে strings.Builder
, আমরা একটি ক্ষুদ্র লাভ 3.5% মধ্যে গতি , কিন্তু আমরা অর্জন 50% মেমরির ব্যবহার এবং বরাদ্দ হ্রাস! ওটা সুন্দর!
পরিশেষে যদি আমরা এর unsafe
পরিবর্তে প্যাকেজটি ব্যবহার করার সাহস করি তবে আমরা strings.Builder
আবার একটি দুর্দান্ত 14% লাভ করব ।
প্রাথমিক সমাধান চূড়ান্ত তুলনা: RandStringBytesMaskImprSrcUnsafe()
হয় 6.3 গুণ দ্রুত তুলনায় RandStringRunes()
, ব্যবহার এক ষষ্ঠ মেমরি এবং কয়েক বরাদ্দ অর্ধেক । কার্যোদ্ধার.
rand.Source
করা ব্যবহৃত হয়েছে। একটি ভাল কার্যসংক্রান্ত একটি পাস হবে rand.Source
থেকে RandStringBytesMaskImprSrc()
ফাংশন, এবং যে ভাবে কোন লকিং প্রয়োজন বোধ করা হয় এবং এর ফলে কর্মক্ষমতা / দক্ষতা প্রভাবিত হয় না। প্রতিটি গোরোটিনের নিজস্ব থাকতে পারে Source
।
defer
যখন আপনার স্পষ্ট প্রয়োজন হয় না তখন এটি ব্যবহার করা এড়ানো উচিত। গ্রুকবাজ.com
defer
লক ডাকার ঠিক আগে বা পরে কোনও মুটেক্সকে আনলক করা আইএমও বেশিরভাগই খুব ভাল ধারণা; আপনি উভয়ই গ্যারান্টিযুক্ত যে আনলক করতে ভুলবেন না তবে এমনকী কোনও অ-মারাত্মক আতঙ্কের মধ্য ফাংশনেও আনলক করতে ভুলবেন না।
আপনি কেবল এটির জন্য কোড লিখতে পারেন। আপনি ইউটিএফ -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))]
}
return string(b)
}
func main() {
rand.Seed(time.Now().UnixNano())
fmt.Println(randSeq(10))
}
rand.Seed(time.Now().Unix())
বাrand.Seed(time.Now().UnixNano())
math/rand
; ব্যবহার crypto/rand
(যেমন @ Not_A_Golfer এর বিকল্প 1) পরিবর্তে।
দুটি সম্ভাব্য বিকল্প (অবশ্যই আরও কিছু হতে পারে):
আপনি এমন crypto/rand
প্যাকেজটি ব্যবহার করতে পারেন যা র্যান্ডম বাইট অ্যারে পড়ার সমর্থন করে (/ dev / urandom থেকে) এবং ক্রিপ্টোগ্রাফিক র্যান্ডম প্রজন্মের দিকে তাকাতে হবে। দেখতে http://golang.org/pkg/crypto/rand/#example_Read । এটি যদিও সাধারণ সিউডো-এলোমেলো সংখ্যা জেনারেশনের চেয়ে ধীর হতে পারে।
এমডি 5 বা এই জাতীয় কিছু ব্যবহার করে একটি এলোমেলো নম্বর নিন এবং হ্যাশ করুন।
icza's
বিস্ময়করভাবে ব্যাখ্যা করা সমাধান অনুসরণ করার পরে , এটির crypto/rand
পরিবর্তে এখানে এটির একটি পরিবর্তন রয়েছে 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++
}
}
return string(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
}
আপনি যদি আরও জেনেরিক সমাধান চান তবে এটি আপনাকে স্ট্রিংটি তৈরি করতে অক্ষর বাইটের স্লাইসে যেতে দেয়, আপনি এটি ব্যবহার করে দেখতে পারেন:
// 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 byte
var bitMask byte
for 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 slice
if idx := int(randomBytes[j%length] & bitMask); idx < availableCharLength {
result[i] = availableCharBytes[idx]
i++
}
}
return string(result)
}
আপনি যদি এলোমেলোভাবে আপনার নিজস্ব উত্সটিতে যেতে চান, তবে এটির io.Reader
পরিবর্তে ব্যবহারের পরিবর্তে উপরেরটি সংশোধন করা তুচ্ছ crypto/rand
।
আপনি যদি ক্রিপ্টোগ্রাফিকভাবে নিরাপদ র্যান্ডম সংখ্যাগুলি চান এবং সঠিক চরসেটটি নমনীয় হয় (বলুন, বেস 64 ভাল) তবে আপনি পছন্দসই আউটপুট আকার থেকে এলোমেলো অক্ষরের দৈর্ঘ্যটি ঠিক কী গণনা করতে পারেন তা গণনা করতে পারেন।
বেস 64 টেক্সটটি বেজ 256 এর তুলনায় 1/3 দীর্ঘ। (2 ^ 8 বনাম 2 ^ 6; 8 বিট / 6 বিট = 1.333 অনুপাত)
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
}
দ্রষ্টব্য: আপনি যদি + এবং / অক্ষর পছন্দ করেন - এবং _ তবে আপনি RawStdEncoding ব্যবহার করতে পারেন
আপনি যদি হেক্সস চান, বেস 16 এর বেজ 256 এর তুলনায় 2x দীর্ঘ। (2 ^ 8 বনাম 2 ^ 4; 8 বিট / 4 বিট = 2 এক্স অনুপাত)
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
}
তবে আপনার অক্ষর সেটের জন্য বেসএন এনকোডারটিতে যদি আপনার একটি বেস 256 থাকে তবে আপনি এটি যেকোন স্বেচ্ছাকৃতির চরিত্র সেটটিতে প্রসারিত করতে পারেন। আপনার চরিত্রের সেটটি উপস্থাপনের জন্য কত বিট প্রয়োজন তার সাথে আপনি একই আকারের গণনা করতে পারেন। যেকোন স্বেচ্ছাসেবীর চরসেটের জন্য অনুপাতের গণনা হ'ল ratio = 8 / log2(len(charset))
:)।
যদিও এই উভয় সমাধানই সুরক্ষিত, সহজ, দ্রুত হওয়া উচিত এবং আপনার ক্রিপ্টো এনট্রপি পুলটি অপচয় করবেন না।
এখানে খেলার মাঠ দেখানো হচ্ছে এটি যে কোনও আকারের জন্য কাজ করে। https://play.golang.org/p/i61WUVR8_3Z
func Rand(n int) (str string) {
b := make([]byte, n)
rand.Read(b)
str = fmt.Sprintf("%x", b)
return
}
[]byte
?
আপনি যদি অনুমতিপ্রাপ্ত অক্ষরগুলির পুলে কয়েকটি অক্ষর যুক্ত করতে ইচ্ছুক হন তবে আপনি কোডটি আইও.আরডিডার এর মাধ্যমে এলোমেলো বাইট সরবরাহ করে এমন কোনও কিছু দিয়ে কাজ করতে পারেন। এখানে আমরা ব্যবহার করছি 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 output
for 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
}
random % 64
প্রয়োজনীয়?
len(encodeURL) == 64
। যদি random % 64
এটি না করা হয় randomPos
তবে> = be৪ হতে পারে এবং রানটাইমের সময় আতঙ্কের বাইরে চলে যেতে পারে।
const (
chars = "0123456789_abcdefghijkl-mnopqrstuvwxyz" //ABCDEFGHIJKLMNOPQRSTUVWXYZ
charsLen = len(chars)
mask = 1<<6 - 1
)
var rng = rand.NewSource(time.Now().UnixNano())
// RandStr 返回指定长度的随机字符串
func RandStr(ln int) string {
/* chars 38个字符
* rng.Int63() 每次产出64bit的随机数,每次我们使用6bit(2^6=64) 可以使用10次
*/
buf := make([]byte, ln)
for idx, cache, remain := ln-1, rng.Int63(), 10; idx >= 0; {
if remain == 0 {
cache, remain = rng.Int63(), 10
}
buf[idx] = chars[int(cache&mask)%charsLen]
cache >>= 6
remain--
idx--
}
return *(*string)(unsafe.Pointer(&buf))
}
বেঞ্চমার্কআর্যান্ডএসটিআর 16-8 20000000 68.1 এনএস / ওপেন 16 বি / ওপ 1 বরাদ্দ / অপ