【问题标题】:How do I use a string as input to the rand.Seed() function in Golang?如何使用字符串作为 Golang 中 rand.Seed() 函数的输入?
【发布时间】:2018-06-26 16:30:43
【问题描述】:

我想做的事:

基于字符串的内容(例如a1b2c3d4e5),我想生成一堆“可重复”的随机事件。在 Golang 中生成可重复的随机数很容易 - 您只需使用 rand.Seed() 函数为随机数生成器播种特定数字,然后就完成了。

但是,rand.Seed() 函数将 int64 作为参数。所以我需要以某种方式将字符串转换为 int64。

我已经尝试过的:

我的第一个想法是使用base64对字符串进行编码,然后将字节序列转换为int64。但是,通过一些似乎只支持大约 7 长度的字符串的基本测试。添加第 8 个字符后,数字保持不变。

我想这里的根本问题是可能的字符串值比可能的 int64 值更多。

那么某种散列函数呢?我知道的大多数哈希函数都会返回一个字节序列;我需要某种返回 int64 的哈希函数。也许这种方法也被误导了?

【问题讨论】:

  • 出于安全原因,是否要求种子不容易从输入字符串中操作/检测?因为如果不是,我想不出任何理由为什么只取字符串的任何散列并应用 mod MAXINT 是不够的。

标签: string go random seed random-seed


【解决方案1】:

使用哈希函数是一个很好的方向。要生成适合 int64 的值,只需获取哈希值的前 8 个字节。

对于一个好的散列函数,散列结果的每个字节(或者更确切地说是每个位)都取决于所有输入字节,因此前 8 个字节在输入一定长度后不会“卡住”。

要将 8 字节的切片转换为 int64 值,您可以使用 encoding/binary 包。

例如:

digest := ... // calculate digest
seed := int64(binary.BigEndian.Uint64(digest[:8]))

也可以省略对摘要值 digest[:8] 的切片,因为 BigEndian.Uint64() 调用只会读取前 8 个字节。

【讨论】:

    【解决方案2】:

    字符串的哈希就可以了。

    你可以使用:

    • md5 字符串的总和(返回 16 个字节)
    • 将前 8 个字节转换为 uint64(binary.BigEndian.Uint64 忽略其余部分。但如果您的输入小于 8 个字节,它将崩溃)。

    示例代码 (playground link):

    package main
    
    import (
        "crypto/md5"
        "encoding/binary"
        "fmt"
        "io"
        "math/rand"
    )
    
    func main() {
        h := md5.New()
        io.WriteString(h, "And Leon's getting larger!")
        var seed uint64 = binary.BigEndian.Uint64(h.Sum(nil))
        fmt.Println(seed)
        rand.Seed(int64(seed))
        fmt.Println(rand.Int())
    }
    

    打印:

    2458341265858962012
    792667671
    

    注意:md5 只是一个例子。您可以使用任何生成至少 8 个字节的散列。例如:sha256。只需将md5.New() 替换为sha256.New()(和导入)。 你可以找到一个很好的哈希示例列表here

    还有一个重要的警告:这根本不涉及加密应用程序。我假设这是用于非加密目的的用户提供的种子(例如:游戏种子)。

    【讨论】:

    • CRC64 不是比 MD5(或 SHA256)更好的散列算法吗,因为它输出正好 64 位,因此不需要忽略/截断任何内容?
    • 那肯定行得通。如果这是用于游戏的随机数生成器之类的东西,那将是完美的。但是,如果这是为加密目的播种随机数生成器,那么 sha256 可能是迄今为止提到的三个中最好的。
    • 用于游戏。您可以编辑 CRC64 的示例吗?
    • CRC64 使用起来有点烦人。您可以在我添加到答案的链接中查看示例(以及 Go 中的许多其他哈希)。
    【解决方案3】:

    你可以试试这个。您可以通过使用时间生成不同的种子来为生成随机事件提供不同的序列。

    package main
    
    import (
        "bytes"
        "fmt"
        "math/rand"
        "time"
    )
    
    var r = rand.New(rand.NewSource(time.Now().UTC().UnixNano()))
    func main() {
        fmt.Println(randomString(10))
    }
    
    func randomString(l int) string {
    
        var result bytes.Buffer
        var temp string
        for i := 0; i < l; {
            if string(randInt(65, 90)) != temp {
                temp = string(randInt(65, 90))
                result.WriteString(temp)
                i++
            }
        }
        return result.String()
    }
    
    func randInt(min int, max int) int {
        return min + r.Intn(max-min)
    }
    

    【讨论】:

      猜你喜欢
      • 2017-12-11
      • 2014-02-11
      • 2020-03-19
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2012-10-05
      • 2016-03-03
      • 1970-01-01
      相关资源
      最近更新 更多