iTranslated by AI

The content below is an AI-generated translation. This is an experimental feature, and may contain errors. View original article
💻

Using crypto/rand as math/rand

に公開

Haven't you ever wondered? Why are the internal structures of math/rand and crypto/rand so completely different? Well, since their purposes are different, it's not surprising that their structures are different, but I've often thought it would be nice if we could at least use crypto/rand as a source for math/rand.

Actually, it's not that difficult to use crypto/rand as a source for math/rand. The definition of a source in math/rand is as follows:

math/rand/rand.go
// A Source represents a source of uniformly-distributed
// pseudo-random int64 values in the range [0, 1<<63).
type Source interface {
    Int63() int64
    Seed(seed int64)
}

// A Source64 is a Source that can also generate
// uniformly-distributed pseudo-random uint64 values in
// the range [0, 1<<64) directly.
// If a Rand r's underlying Source s implements Source64,
// then r.Uint64 returns the result of one call to s.Uint64
// instead of making two calls to s.Int63.
type Source64 interface {
    Source
    Uint64() uint64
}

Since it is defined this way, all we need to do is create a wrapper that matches it. For example, something like this:

sample.go
type Source struct{}

// Seed method is dummy function for rand.Source interface.
func (s Source) Seed(seed int64) {}

// Uint64 method generates a random number in the range [0, 1<<64).
func (s Source) Uint64() uint64 {
    b := [8]byte{}
    ct, _ := rand.Read(b[:])
    return binary.BigEndian.Uint64(b[:ct])
}

// Int63 method generates a random number in the range [0, 1<<63).
func (s Source) Int63() int64 {
    return (int64)(s.Uint64() >> 1)
}

By doing this,

sample.go
fmt.Println(rand.New(Source{}).Float64()) // 0.9581627789424901

you can use the methods provided by the rand.Rand type in this way[1]. The full code looks like this.

sample.go
//go:build run
// +build run

package main

import (
    "crypto/rand"
    "encoding/binary"
    "fmt"
    mrand "math/rand"
)

type Source struct{}

// Seed method is dummy function for rand.Source interface.
func (s Source) Seed(seed int64) {}

// Uint64 method generates a random number in the range [0, 1<<64).
func (s Source) Uint64() uint64 {
    b := [8]byte{}
    ct, _ := rand.Read(b[:])
    return binary.BigEndian.Uint64(b[:ct])
}

// Int63 method generates a random number in the range [0, 1<<63).
func (s Source) Int63() int64 {
    return (int64)(s.Uint64() >> 1)
}

func main() {
    fmt.Println(mrand.New(Source{}).Float64())
}

So, I decided to package this. That said, it seemed like a waste to create a whole repository for such a small feature, so I integrated it as a bonus feature into my own pseudo-random number package, github.com/goark/mt. You can use it like this:

//go:build run
// +build run

package main

import (
    "fmt"
    "math/rand"

    "github.com/goark/mt/secure"
)

func main() {
    fmt.Println(rand.New(secure.Source{}).Uint64())
}

All right, yep, yep, okay. Well, time to get back to work.

https://text.baldanders.info/release/mersenne-twister-by-golang/

脚注
  1. Please note that the methods provided by the rand.Rand type are not concurrency-safe. ↩︎

GitHubで編集を提案

Discussion