如何使用字符串作为 Golang 中 rand.Seed() 函数的输入?
How do I use a string as input to the rand.Seed() function in Golang?
我想做的事情:
基于字符串的内容(例如a1b2c3d4e5
),我想生成一堆"repeatable"随机事件。在 Golang 中生成可重复的随机数很容易——您只需使用 rand.Seed() 函数为随机数生成器播种特定的数字,然后就完成了。
但是,rand.Seed() 函数将 int64 作为参数。所以我需要以某种方式将字符串转换为 int64。
我已经尝试过:
我的第一个想法是使用 base64 对字符串进行编码,然后将字节序列转换为 int64。但是,通过一些基本测试,似乎只支持长度为 7 左右的字符串。添加第 8 个字符后,数字保持不变。
我想这里的根本问题是可能的字符串值多于可能的 int64 值。
那么某种哈希函数呢?我所知道的大多数哈希函数 return 一个字节序列;我需要某种 return 是 int64 的散列函数。也许这种方法也被误导了?
使用散列函数是一个很好的方向。要生成适合 int64
的值,只需取散列值的前 8 个字节即可。
对于一个好的散列函数,散列结果的每个字节(或更确切地说是每个位)都取决于所有输入字节,因此前 8 个字节在一定的输入长度后不会得到 "stuck" .
要将 8 字节的切片转换为 int64
值,您可以使用 encoding/binary
包。
例如:
digest := ... // calculate digest
seed := int64(binary.BigEndian.Uint64(digest[:8]))
切片摘要值 digest[:8]
也可以省略,因为 BigEndian.Uint64()
调用只会读取前 8 个字节。
字符串的哈希值就可以了。
您可以使用:
- md5 字符串总和(returns 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.
还有一句重要的警告:这根本不涉及加密应用程序。我假设这是用于非加密目的的用户提供的种子(例如:游戏种子)。
你可以试试这个。您可以通过使用时间生成不同的种子来为生成随机事件提供不同的序列。
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)
}
我想做的事情:
基于字符串的内容(例如a1b2c3d4e5
),我想生成一堆"repeatable"随机事件。在 Golang 中生成可重复的随机数很容易——您只需使用 rand.Seed() 函数为随机数生成器播种特定的数字,然后就完成了。
但是,rand.Seed() 函数将 int64 作为参数。所以我需要以某种方式将字符串转换为 int64。
我已经尝试过:
我的第一个想法是使用 base64 对字符串进行编码,然后将字节序列转换为 int64。但是,通过一些基本测试,似乎只支持长度为 7 左右的字符串。添加第 8 个字符后,数字保持不变。
我想这里的根本问题是可能的字符串值多于可能的 int64 值。
那么某种哈希函数呢?我所知道的大多数哈希函数 return 一个字节序列;我需要某种 return 是 int64 的散列函数。也许这种方法也被误导了?
使用散列函数是一个很好的方向。要生成适合 int64
的值,只需取散列值的前 8 个字节即可。
对于一个好的散列函数,散列结果的每个字节(或更确切地说是每个位)都取决于所有输入字节,因此前 8 个字节在一定的输入长度后不会得到 "stuck" .
要将 8 字节的切片转换为 int64
值,您可以使用 encoding/binary
包。
例如:
digest := ... // calculate digest
seed := int64(binary.BigEndian.Uint64(digest[:8]))
切片摘要值 digest[:8]
也可以省略,因为 BigEndian.Uint64()
调用只会读取前 8 个字节。
字符串的哈希值就可以了。
您可以使用:
- md5 字符串总和(returns 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.
还有一句重要的警告:这根本不涉及加密应用程序。我假设这是用于非加密目的的用户提供的种子(例如:游戏种子)。
你可以试试这个。您可以通过使用时间生成不同的种子来为生成随机事件提供不同的序列。
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)
}