How to properly seed random number generator

前端 未结 9 1108
陌清茗
陌清茗 2020-11-28 20:25

I am trying to generate a random string in Go and here is the code I have written so far:

package main

import (
    \"bytes\"
    \"fmt\"
    \"math/rand\"
         


        
相关标签:
9条回答
  • 2020-11-28 21:02

    It's nano seconds, what are the chances of getting the same seed twice.
    Anyway, thanks for the help, here is my end solution based on all the input.

    package main
    
    import (
        "math/rand"
        "time"
    )
    
    func init() {
        rand.Seed(time.Now().UTC().UnixNano())
    }
    
    // generates a random string
    func srand(min, max int, readable bool) string {
    
        var length int
        var char string
    
        if min < max {
            length = min + rand.Intn(max-min)
        } else {
            length = min
        }
    
        if readable == false {
            char = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789"
        } else {
            char = "ABCDEFHJLMNQRTUVWXYZabcefghijkmnopqrtuvwxyz23479"
        }
    
        buf := make([]byte, length)
        for i := 0; i < length; i++ {
            buf[i] = char[rand.Intn(len(char)-1)]
        }
        return string(buf)
    }
    
    // For testing only
    func main() {
        println(srand(5, 5, true))
        println(srand(5, 5, true))
        println(srand(5, 5, true))
        println(srand(5, 5, false))
        println(srand(5, 7, true))
        println(srand(5, 10, false))
        println(srand(5, 50, true))
        println(srand(5, 10, false))
        println(srand(5, 50, true))
        println(srand(5, 10, false))
        println(srand(5, 50, true))
        println(srand(5, 10, false))
        println(srand(5, 50, true))
        println(srand(5, 4, true))
        println(srand(5, 400, true))
        println(srand(6, 5, true))
        println(srand(6, 5, true))
        println(srand(6, 5, true))
        println(srand(6, 5, true))
        println(srand(6, 5, true))
        println(srand(6, 5, true))
        println(srand(6, 5, true))
        println(srand(6, 5, true))
        println(srand(6, 5, true))
        println(srand(6, 5, true))
        println(srand(6, 5, true))
        println(srand(6, 5, true))
    }
    
    0 讨论(0)
  • 2020-11-28 21:05

    I don't understand why people are seeding with a time value. This has in my experience never been a good idea. For example, while the system clock is maybe represented in nanoseconds, the system's clock precision isn't nanoseconds.

    This program should not be run on the Go playground but if you run it on your machine you get a rough estimate on what type of precision you can expect. I see increments of about 1000000 ns, so 1 ms increments. That's 20 bits of entropy that are not used. All the while the high bits are mostly constant!? Roughly ~24 bits of entropy over a day which is very brute forceable (which can create vulnerabilities).

    The degree that this matters to you will vary but you can avoid pitfalls of clock based seed values by simply using the crypto/rand.Read as source for your seed. It will give you that non-deterministic quality that you are probably looking for in your random numbers (even if the actual implementation itself is limited to a set of distinct and deterministic random sequences).

    import (
        crypto_rand "crypto/rand"
        "encoding/binary"
        math_rand "math/rand"
    )
    
    func init() {
        var b [8]byte
        _, err := crypto_rand.Read(b[:])
        if err != nil {
            panic("cannot seed math/rand package with cryptographically secure random number generator")
        }
        math_rand.Seed(int64(binary.LittleEndian.Uint64(b[:])))
    }
    

    As a side note but in relation to your question. You can create your own rand.Source using this method to avoid the cost of having locks protecting the source. The rand package utility functions are convenient but they also use locks under the hood to prevent the source from being used concurrently. If you don't need that you can avoid it by creating your own Source and use that in a non-concurrent way. Regardless, you should NOT be reseeding your random number generator between iterations, it was never designed to be used that way.

    0 讨论(0)
  • 2020-11-28 21:05

    I tried the program below and saw different string each time

    package main
    
    import (
        "fmt"
        "math/rand"
        "time"
    )
    
    func RandomString(count int){
      rand.Seed(time.Now().UTC().UnixNano()) 
      for(count > 0 ){
        x := Random(65,91)
        fmt.Printf("%c",x)
        count--;
      }
    }
    
    func Random(min, max int) (int){
     return min+rand.Intn(max-min) 
    }
    
    func main() {
     RandomString(12)
    }
    

    And the output on my console is

    D:\james\work\gox>go run rand.go
    JFBYKAPEBCRC
    D:\james\work\gox>go run rand.go
    VDUEBIIDFQIB
    D:\james\work\gox>go run rand.go
    VJYDQPVGRPXM
    
    0 讨论(0)
提交回复
热议问题