generate a random bool in go
Examples of how to generate a random bool
value can be found here (not necessarily the fastest solutions, as that wasn't a requirement there):
How can I let a function randomly return either a true or a false in go
The slowest part of such algorithms is always getting the random data (random information). For example a rand.Int31()
call returns 31 random bits, but if we only use it to "generate" a random bool
value (which is 1 bit of information), we waste 30 bits (which could be 30 additional random bool
values!).
Using rand.Source
is a good choice, as we don't need all the "code kung-fu" that rand.Rand
does on the random data. We just need a source of random information.
rand.Source
defines one method to obtain random information:
Int63() int64
This Source.Int63()
method returns 63 random bits; to be fast(est), we should use all. Of course generating a single bool
value requires only 1 of its bits, but we should store the remaining and use them when subsequent random bool
s are asked from us.
This is how it can be done:
type boolgen struct {
src rand.Source
cache int64
remaining int
}
func (b *boolgen) Bool() bool {
if b.remaining == 0 {
b.cache, b.remaining = b.src.Int63(), 63
}
result := b.cache&0x01 == 1
b.cache >>= 1
b.remaining--
return result
}
Creating such a boolgen
is like this:
func New() *boolgen {
return &boolgen{src: rand.NewSource(time.Now().UnixNano())}
}
Example usage:
r := New()
for i := 0; i < 100; i++ {
if i%10 == 0 {
fmt.Println()
}
fmt.Print(r.Bool(), " ")
}
Example output (try it on the Go Playground):
false false true true false false false false false false
false false false true false false true false true true
false false true false true false false true true true
false false false false false false false true true false
true true true true false false false false true false
true true true false true true true true true true
true true false true true false false true false true
true true false false false true true true true false
true false false true true true true false false true
true false false false false false false false true false
Some notes:
The Source
returned by rand.NewSource()
is not safe for concurrent use by multiple goroutines, so our boolgen
is also not safe for concurrent use. On one hand this is good, as it will be faster (as no synchronization takes place) than using the default source of the rand
package which is safe in this manner (which is by the way unexported, so can only be "reached" indirectly through functions of the rand
package).
If you need to use this from multiple goroutines, fastest (as in spirit of the question) would be for all goroutines to create their own boolgen
, so no synchronization is needed.
If boolgen
itself must be made safe for concurrent use, simply its Bool()
method should be protected with a sync.Mutex
.
I did a speed comparison of different methods using math/rand
package from stdlib and github.com/MichaelTJones/pcg
, another pseudorandom number generator in go
Here is the code I used for timing the different variants:
package main
import (
"fmt"
"math/rand"
"testing"
"github.com/MichaelTJones/pcg"
)
func BenchmarkBool(b *testing.B) {
pcg32 := pcg.NewPCG32()
ff := []func() bool{
func() bool { return rand.Intn(2) == 0 }, // 1
func() bool { return rand.Int31n(2) == 0 }, // 2
func() bool { return rand.Int63n(2) == 0 }, // 3
func() bool { return rand.Float32() < .5 }, // 4
func() bool { return rand.Float64() < .5 }, // 5
func() bool { return rand.Int31()&(1<<30) == 0 }, // 6
func() bool { return rand.Uint32()&(1<<31) == 0 }, // 7
func() bool { return rand.Int63()&(1<<62) == 0 }, // 8
func() bool { return rand.Uint64()&(1<<63) == 0 }, // 9
func() bool { return pcg32.Random()&0x01 == 0 }, // 10
}
for i, f := range ff {
b.Run(fmt.Sprintf("method%v", i+1), func(b *testing.B) {
for n := 0; n < b.N; n++ {
_ = f()
}
})
}
}
On my machine, the output of this program is
BenchmarkBool/method1-4 50000000 36.8 ns/op
BenchmarkBool/method2-4 50000000 34.7 ns/op
BenchmarkBool/method3-4 50000000 31.5 ns/op
BenchmarkBool/method4-4 50000000 33.3 ns/op
BenchmarkBool/method5-4 50000000 30.1 ns/op
BenchmarkBool/method6-4 50000000 29.4 ns/op
BenchmarkBool/method7-4 50000000 31.0 ns/op
BenchmarkBool/method8-4 50000000 28.7 ns/op
BenchmarkBool/method9-4 50000000 29.5 ns/op
BenchmarkBool/method10-4 300000000 4.86 ns/op
i.e. method number 10 is fastest, and number 1 is slowest.
I am just a rookie, but this makes more sense to me than other solutions provided:
package randbool
import (
"math/rand"
"time"
)
/*
RandBool
This function returns a random boolean value based on the current time
*/
func RandBool() bool {
rand.Seed(time.Now().UnixNano())
return rand.Intn(2) == 1
}
I am not too familiar with go though, so please forgive my formating.