গোয়ে স্থির দৈর্ঘ্যের এলোমেলো স্ট্রিং কীভাবে উত্পন্ন করা যায়?


300

আমি যেতে চাই কেবলমাত্র অক্ষরের একটি এলোমেলো স্ট্রিং (বড় হাতের বা ছোট হাতের অক্ষর), কোনও সংখ্যা নেই Go এটি করার দ্রুত এবং সহজ উপায় কী?


2
@ ভিনসেমিঘ: এখানে মূল বিষয়গুলি নিয়ে আলোচনা করার একটি মেটা বিষয়। meta.stackoverflow.com/q/274645/395461 ব্যক্তিগতভাবে, আমি মনে করি বেসিক প্রশ্নগুলি যদি ভাল লেখা হয় এবং বিষয়টিতে থাকে তবে তা ঠিক আছে। নীচের উত্তরগুলি দেখুন, তারা এমন অনেকগুলি বিষয় চিত্রিত করেছেন যা নতুন কারওর জন্য কার্যকর হবে। লুপ টাইপ ঢালাই, করুন (), ইত্যাদি জন্য
শ্যানন ম্যাথিউজ

2
@ শ্যানন " এই প্রশ্নটি কোনও গবেষণার প্রচেষ্টা দেখায় না " (আপনার লিঙ্কে প্রথম অতি উচ্চারণের উত্তর) - এটিই আমি উল্লেখ করছি। তিনি কোন গবেষণা প্রচেষ্টা দেখায় না। মোটেও কোনও প্রচেষ্টা (একটি প্রচেষ্টা, বা এমনকি তিনি অনলাইনে দেখেছেন, যা সম্ভবত তিনি তা করেননি বলে উল্লেখ করেছেন)। যদিও এটি নতুন কারও জন্য উপকারী হবে , তবে এই সাইটটি নতুন লোকদের শেখানোর উপর দৃষ্টি নিবদ্ধ করে না। এটি টিউটোরিয়াল / গাইড নয়, নির্দিষ্ট প্রোগ্রামিং সমস্যা / প্রশ্নের উত্তর দেওয়ার উপর দৃষ্টি নিবদ্ধ করেছে। যদিও এটি পরবর্তীকালের জন্য ব্যবহার করা যেতে পারে, এটি ফোকাস নয়, এবং এইভাবে এই প্রশ্নটি বন্ধ করা উচিত। পরিবর্তে, এর চামচজাত /:
ভিন্স এমি

9
@ ভিনসেমিহ আমি এই প্রশ্নটি এক বছর আগে জিজ্ঞাসা করেছি। আমি এলোমেলো স্ট্রিংগুলির জন্য অনলাইনে অনুসন্ধান করেছি এবং ডকগুলিও পড়েছি। তবে এটি সহায়ক ছিল না। আমি যদি প্রশ্নটিতে না লিখে থাকি, তবে এর অর্থ এই নয় যে আমি গবেষণা করেছি না।
আনিস শাহ '

উত্তর:


808

পলের সমাধানটি একটি সাধারণ , সাধারণ সমাধান সরবরাহ করে।

প্রশ্নটি "দ্রুততম এবং সহজতম উপায়" জন্য জিজ্ঞাসা করে । আসুন দ্রুততম অংশটিও সম্বোধন করি। আমরা আমাদের চূড়ান্ত, দ্রুততম কোডটি পুনরাবৃত্ত পদ্ধতিতে পৌঁছে যাব। প্রতিটি পুনরাবৃত্তির মানদণ্ড উত্তরের শেষে পাওয়া যাবে।

সমস্ত সমাধান এবং বেঞ্চমার্কিং কোড গো প্লেগ্রাউন্ডে পাওয়া যাবে । প্লেগ্রাউন্ডের কোডটি একটি পরীক্ষামূলক ফাইল, কোনও এক্সিকিউটেবল নয়। আপনাকে এটি নামের একটি ফাইলে সংরক্ষণ করতে হবে এবং XX_test.goএটি দিয়ে চালাতে হবে

go test -bench . -benchmem

মূল শব্দ :

আপনার কেবল একটি এলোমেলো স্ট্রিং প্রয়োজন হলে দ্রুততম সমাধানটি গ-টু সমাধান নয়। তার জন্য, পলের সমাধান নিখুঁত। এটি যদি পারফরম্যান্সে গুরুত্বপূর্ণ হয়। যদিও প্রথম 2 টি পদক্ষেপ ( বাইটস এবং রিমাইন্ডার ) একটি গ্রহণযোগ্য সমঝোতা হতে পারে: তারা 50% এর মত পারফরম্যান্সকে উন্নত করে ( দ্বিতীয়টিতে সঠিক সংখ্যা দেখুন Ben বেঞ্চমার্ক বিভাগে), এবং তারা জটিলতা উল্লেখযোগ্যভাবে বাড়ায় না।

এটি বলার পরেও, আপনার যদি দ্রুততম সমাধানের প্রয়োজন না হয়, এই উত্তরের মাধ্যমে পড়া পড়া সাহসিক এবং শিক্ষামূলক হতে পারে।

I. উন্নতি

1. জেনেসিস (রুনস)

অনুস্মারক হিসাবে, আসল, সাধারণ সমাধান যা আমরা উন্নতি করছি তা হ'ল:

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

2. বাইট

অক্ষরটি চয়ন করতে এবং এলোমেলোভাবে বর্ণমালা স্ট্রিংয়ে ইংরেজি বর্ণমালার কেবলমাত্র বড় হাতের এবং ছোট হাতের অক্ষর থাকে তবে আমরা কেবল বাইট দিয়ে কাজ করতে পারি কারণ ইংলিশ বর্ণমালা অক্ষর ইউটিএফ -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 বিটে পৌঁছায়, তা নগন্য নয়।

4. মাস্কিং

পূর্ববর্তী সমাধানের উপর ভিত্তি করে, আমরা কেবলমাত্র এলোমেলো সংখ্যার ন্যূনতম বিটগুলির মধ্যে যতগুলি অক্ষরের সংখ্যা উপস্থাপন করতে হয় প্রয়োজনীয় হিসাবে অক্ষরের সমান বন্টন বজায় রাখতে পারি। সুতরাং উদাহরণস্বরূপ, যদি আমরা 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)
}

5. মাস্কিং উন্নত

পূর্ববর্তী সমাধানটি কেবলমাত্র 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)
}

6. উত্স

কাচ উন্নত , বেশ ভাল অনেক আমরা এটি উন্নত করতে পারেন। আমরা পারতাম, তবে জটিলতার জন্য মূল্যবান নয়।

এবার উন্নতি করার জন্য আরও কিছু খুঁজে বের করা যাক। এলোমেলো সংখ্যার উত্স।

এখানে একটি 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দ্বারা ফিরে আসা দ্রুততর হওয়ার সম্ভাবনা বেশি থাকে)।

7. ব্যবহার 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()পদ্ধতিটি কল করেছি, এটি নিশ্চিত করে যে এটি একটি যথেষ্ট পরিমাণে অভ্যন্তরীণ স্লাইস বরাদ্দ করেছে (আমরা এলোমেলো অক্ষরগুলি যুক্ত করার সাথে সাথে পুনঃনির্মাণগুলি এড়াতে)।

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

(9. ব্যবহার করে 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(), ব্যবহার এক ষষ্ঠ মেমরি এবং কয়েক বরাদ্দ অর্ধেক । কার্যোদ্ধার.


8
@ রবিভি ইউপ, কারণ একটি ভাগ rand.Sourceকরা ব্যবহৃত হয়েছে। একটি ভাল কার্যসংক্রান্ত একটি পাস হবে rand.Sourceথেকে RandStringBytesMaskImprSrc()ফাংশন, এবং যে ভাবে কোন লকিং প্রয়োজন বোধ করা হয় এবং এর ফলে কর্মক্ষমতা / দক্ষতা প্রভাবিত হয় না। প্রতিটি গোরোটিনের নিজস্ব থাকতে পারে Source
আইকজা

113
@ আইকিজা, এসও-তে দীর্ঘ সময় দেখলাম এটি সেরা উত্তরগুলির মধ্যে একটি!
অ্যাস্ট্রোপ্যানিক

1
@ মাইক অ্যাটলাস: deferযখন আপনার স্পষ্ট প্রয়োজন হয় না তখন এটি ব্যবহার করা এড়ানো উচিত। গ্রুকবাজ.com
জ্যান লিনেক্স

1
টিপটির জন্য @ZanLynx thx; যদিও deferলক ডাকার ঠিক আগে বা পরে কোনও মুটেক্সকে আনলক করা আইএমও বেশিরভাগই খুব ভাল ধারণা; আপনি উভয়ই গ্যারান্টিযুক্ত যে আনলক করতে ভুলবেন না তবে এমনকী কোনও অ-মারাত্মক আতঙ্কের মধ্য ফাংশনেও আনলক করতে ভুলবেন না।
মাইক অ্যাটলাস

1
@ রবিবিভি দেখে মনে হচ্ছে এই কোডটি থ্রেড / গোরআটাইন নিরাপদ কারণ অন্তর্নিহিত ভাগ করা উত্সটি ইতিমধ্যে একটি লকডসোর্স যা মূটেক্স প্রয়োগ করে ( গোলং.আর.জি.এস.সি.আর.সি. / ম্যাথ / ক্রান্ড / ক্রেডিট.অগ্রহীকরণ 59 )।
আদিত্যযোন

130

আপনি কেবল এটির জন্য কোড লিখতে পারেন। আপনি ইউটিএফ -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))
}

30
র‌্যান্ড। সিড () সম্পর্কে ভুলে যাবেন না, অন্যথায় আপনি প্রতিবার প্রথমবারের মতো একই স্ট্রিংটি পেয়েছেন ... র্যান্ড.সীদ (সময়.নউ ()। ইউটিসি ()। ইউনিক্স ন্যানো ())
ইভান লিন

2
ইভানের সংযোজন সঠিক, তবে অন্যান্য অনুরূপ বিকল্প রয়েছে: rand.Seed(time.Now().Unix())বাrand.Seed(time.Now().UnixNano())
ওপেনওনক

7
কঠোর অনুমানের গোপনের জন্য - একটি পাসওয়ার্ড, একটি ক্রিপ্টো কী ইত্যাদি ---- কখনও ব্যবহার করবেন না math/rand; ব্যবহার crypto/rand(যেমন @ Not_A_Golfer এর বিকল্প 1) পরিবর্তে।
twotwotwo

1
@ ইভানলিন এই অনুমানযোগ্য হবে না? যদি আমাকে জেনারেটরের বীজ বজায় রাখতে হয়, তবে আক্রমণকারী অনুমান করতে পারে যে আমি যে সময়টি বীজ বুনচ্ছি এবং একই আউটপুটটি আমি উত্পন্ন করছি তার পূর্বাভাস দিতে পারি।
মতেজ

4
নোট করুন যে আপনি যদি খেলার মাঠের উপরের বীজ সহ উপরের প্রোগ্রামটি চেষ্টা করে থাকেন তবে এটি সর্বদা একই ফলাফল ফিরে আসবে। আমি খেলার মাঠে এটি চেষ্টা করে যাচ্ছিলাম এবং কিছুক্ষণ পরে এটি বুঝতে পেরেছিলাম। এটা অন্যথায় আমার জন্য ভাল কাজ করে। আশা করি এটি কারওর সময় সাশ্রয় করেছে :)
গৌরব সিনহা

18

প্যাকেজ ইউনিউরি ব্যবহার করুন , যা ক্রিপ্টোগ্রাফিকভাবে সুরক্ষিত ইউনিফর্ম (নিরপেক্ষ) স্ট্রিং উত্পন্ন করে।

দাবি অস্বীকার: আমি প্যাকেজের লেখক


1
পাশে: লেখক, ডেকেস্ট, একটি দুর্দান্ত বিকাশকারী এবং এর মতো অনেকগুলি ছোট, দরকারী প্যাকেজ তৈরি করেছেন।
রোশাম্বো

16

দুটি সম্ভাব্য বিকল্প (অবশ্যই আরও কিছু হতে পারে):

  1. আপনি এমন crypto/randপ্যাকেজটি ব্যবহার করতে পারেন যা র্যান্ডম বাইট অ্যারে পড়ার সমর্থন করে (/ dev / urandom থেকে) এবং ক্রিপ্টোগ্রাফিক র্যান্ডম প্রজন্মের দিকে তাকাতে হবে। দেখতে http://golang.org/pkg/crypto/rand/#example_Read । এটি যদিও সাধারণ সিউডো-এলোমেলো সংখ্যা জেনারেশনের চেয়ে ধীর হতে পারে।

  2. এমডি 5 বা এই জাতীয় কিছু ব্যবহার করে একটি এলোমেলো নম্বর নিন এবং হ্যাশ করুন।


4

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


2

আপনি যদি ক্রিপ্টোগ্রাফিকভাবে নিরাপদ র্যান্ডম সংখ্যাগুলি চান এবং সঠিক চরসেটটি নমনীয় হয় (বলুন, বেস 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


উল্লেখযোগ্য যে গো প্লেগ্রাউন্ড সবসময় একই র্যান্ডম নম্বরটি দেয়, তাই আপনি সেই
কোডটির


1

এখানে আমার উপায়) আপনার ইচ্ছামত গণিতের র্যান্ড বা ক্রিপ্টো র্যান্ড ব্যবহার করুন।

func randStr(len int) string {
    buff := make([]byte, len)
    rand.Read(buff)
    str := base64.StdEncoding.EncodeToString(buff)
    // Base 64 can be longer than len
    return str[:len]
}

0

আপনি যদি অনুমতিপ্রাপ্ত অক্ষরগুলির পুলে কয়েকটি অক্ষর যুক্ত করতে ইচ্ছুক হন তবে আপনি কোডটি আইও.আরডিডার এর মাধ্যমে এলোমেলো বাইট সরবরাহ করে এমন কোনও কিছু দিয়ে কাজ করতে পারেন। এখানে আমরা ব্যবহার করছি 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প্রয়োজনীয়?
সুং চ

2
কারণ len(encodeURL) == 64। যদি random % 64এটি না করা হয় randomPosতবে> = be৪ হতে পারে এবং রানটাইমের সময় আতঙ্কের বাইরে চলে যেতে পারে।
0xcaff 15

-2
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 বরাদ্দ / অপ

আমাদের সাইট ব্যবহার করে, আপনি স্বীকার করেছেন যে আপনি আমাদের কুকি নীতি এবং গোপনীয়তা নীতিটি পড়েছেন এবং বুঝতে পেরেছেন ।
Licensed under cc by-sa 3.0 with attribution required.