Go: math: rand: not random.

Created on 5 Sep 2017  路  10Comments  路  Source: golang/go

Please answer these questions before submitting your issue. Thanks!

What version of Go are you using (go version)?

[jkayser@oel7latest exercise7.3]$ go version
go version go1.9 linux/amd64
[jkayser@oel7latest exercise7.3]$

Does this issue reproduce with the latest release?

Yes.

What operating system and processor architecture are you using (go env)?

[jkayser@oel7latest exercise7.3]$ go env
GOARCH="amd64"
GOBIN=""
GOEXE=""
GOHOSTARCH="amd64"
GOHOSTOS="linux"
GOOS="linux"
GOPATH="/home/jkayser"
GORACE=""
GOROOT="/usr/local/go1.9"
GOTOOLDIR="/usr/local/go1.9/pkg/tool/linux_amd64"
GCCGO="gccgo"
CC="gcc"
GOGCCFLAGS="-fPIC -m64 -pthread -fmessage-length=0 -fdebug-prefix-map=/tmp/go-build505664125=/tmp/go-build -gno-record-gcc-switches"
CXX="g++"
CGO_ENABLED="1"
CGO_CFLAGS="-g -O2"
CGO_CPPFLAGS=""
CGO_CXXFLAGS="-g -O2"
CGO_FFLAGS="-g -O2"
CGO_LDFLAGS="-g -O2"
PKG_CONFIG="pkg-config"
[jkayser@oel7latest exercise7.3]$

What did you do?

If possible, provide a recipe for reproducing the error.
A complete runnable program is good.
A link on play.golang.org is best.

I was doing Exercise 7.3 in the Go Programming Language book. It has you modify the code from section 4.4 (treesort). When I added code to dump the tree structure (prior to sorting) it dumps the same structure every time. I think the structure is supposed to be randomly populated, but the math.random is generating the same random numbers every time.

https://github.com/adonovan/gopl.io/blob/master/ch4/treesort/sort_test.go

What did you expect to see?

Somewhat ramdomness in the generated numbers, even if they were not cryptographically secure.

What did you see instead?

The same random numbers generated every time.

FrozenDueToAge

All 10 comments

[jkayser@oel7latest exercise7.3]$ cat treesort_test.go
// Copyright 漏 2016 Alan A. A. Donovan & Brian W. Kernighan.
// License: https://creativecommons.org/licenses/by-nc-sa/4.0/

package treesort_test

import (
"fmt"
"math/rand"
"sort"
"testing"

"treesort"

)

func TestSort(t *testing.T) {
data := make([]int, 50)
for i := range data {
data[i] = rand.Int() % 50
}

fmt.Println( treesort.GetTree(data) )

treesort.Sort(data)

if !sort.IntsAreSorted(data) {
    t.Errorf("not sorted: %v", data)
}

fmt.Println( treesort.GetTree(data) )

}

[jkayser@oel7latest exercise7.3]$

[jkayser@oel7latest exercise7.3]$ cat src/treesort/treesort.go
// Copyright 漏 2016 Alan A. A. Donovan & Brian W. Kernighan.
// License: https://creativecommons.org/licenses/by-nc-sa/4.0/

// See page 101.

// Package treesort provides insertion sort using an unbalanced binary tree.
package treesort

import (
"strconv"
)

//!+
type Tree struct {
value int
left, right *Tree
}

// Sort sorts values in place.
func Sort(values []int) {
var root *Tree
for _, v := range values {
root = add(root, v)
}
appendValues(values[:0], root)
}

func GetTree(values []int) (*Tree) {
var root *Tree
for _, v := range values {
root = add(root, v)
}
return (root)
}

// appendValues appends the elements of t to values in order
// and returns the resulting slice.
func appendValues(values []int, t *Tree) []int {
if t != nil {
values = appendValues(values, t.left)
values = append(values, t.value)
values = appendValues(values, t.right)
}
return values
}

func add(t *Tree, value int) *Tree {
if t == nil {
// Equivalent to return &Tree{value: value}.
t = new(Tree)
t.value = value
return t
}
if value < t.value {
t.left = add(t.left, value)
} else {
t.right = add(t.right, value)
}
return t
}

//!-

func (t Tree) String() string {
value := "{"value":" + strconv.Itoa((
t).value) + ","
if (t).left != nil {
value = value + ""left":" + (
t).left.String() + ","
} else {
value = value + ""left":null,"
}
if (t).right != nil {
value = value + ""right":" + (
t).right.String()
} else {
value = value + ""right":null"
}
value = value + "}"
return value
}
[jkayser@oel7latest exercise7.3]$

[jkayser@oel7latest exercise7.3]$ cat run.sh

GOPATH="pwd:$GOPATH" go test

[jkayser@oel7latest exercise7.3]$ ./run.sh
{"value":10,"left":{"value":1,"left":{"value":0,"left":null,"right":null},"right":{"value":1,"left":null,"right":{"value":8,"left":{"value":6,"left":{"value":3,"left":{"value":2,"left":null,"right":null},"right":null},"right":{"value":7,"left":null,"right":{"value":7,"left":null,"right":null}}},"right":{"value":9,"left":null,"right":{"value":9,"left":null,"right":null}}}}},"right":{"value":21,"left":{"value":20,"left":{"value":16,"left":{"value":15,"left":{"value":11,"left":{"value":10,"left":null,"right":null},"right":{"value":12,"left":null,"right":null}},"right":{"value":15,"left":null,"right":null}},"right":{"value":18,"left":{"value":16,"left":null,"right":null},"right":{"value":19,"left":null,"right":null}}},"right":{"value":20,"left":null,"right":null}},"right":{"value":37,"left":{"value":34,"left":{"value":24,"left":{"value":23,"left":{"value":22,"left":{"value":21,"left":null,"right":{"value":21,"left":null,"right":null}},"right":null},"right":{"value":23,"left":null,"right":{"value":23,"left":null,"right":null}}},"right":{"value":31,"left":{"value":28,"left":null,"right":{"value":30,"left":null,"right":null}},"right":{"value":33,"left":{"value":32,"left":null,"right":null},"right":null}}},"right":{"value":36,"left":null,"right":null}},"right":{"value":48,"left":{"value":37,"left":null,"right":{"value":41,"left":{"value":40,"left":{"value":37,"left":null,"right":{"value":38,"left":null,"right":{"value":39,"left":null,"right":null}}},"right":null},"right":{"value":44,"left":{"value":43,"left":null,"right":null},"right":{"value":47,"left":null,"right":null}}}},"right":{"value":49,"left":null,"right":null}}}}}
{"value":0,"left":null,"right":{"value":1,"left":null,"right":{"value":1,"left":null,"right":{"value":2,"left":null,"right":{"value":3,"left":null,"right":{"value":6,"left":null,"right":{"value":7,"left":null,"right":{"value":7,"left":null,"right":{"value":8,"left":null,"right":{"value":9,"left":null,"right":{"value":9,"left":null,"right":{"value":10,"left":null,"right":{"value":10,"left":null,"right":{"value":11,"left":null,"right":{"value":12,"left":null,"right":{"value":15,"left":null,"right":{"value":15,"left":null,"right":{"value":16,"left":null,"right":{"value":16,"left":null,"right":{"value":18,"left":null,"right":{"value":19,"left":null,"right":{"value":20,"left":null,"right":{"value":20,"left":null,"right":{"value":21,"left":null,"right":{"value":21,"left":null,"right":{"value":21,"left":null,"right":{"value":22,"left":null,"right":{"value":23,"left":null,"right":{"value":23,"left":null,"right":{"value":23,"left":null,"right":{"value":24,"left":null,"right":{"value":28,"left":null,"right":{"value":30,"left":null,"right":{"value":31,"left":null,"right":{"value":32,"left":null,"right":{"value":33,"left":null,"right":{"value":34,"left":null,"right":{"value":36,"left":null,"right":{"value":37,"left":null,"right":{"value":37,"left":null,"right":{"value":37,"left":null,"right":{"value":38,"left":null,"right":{"value":39,"left":null,"right":{"value":40,"left":null,"right":{"value":41,"left":null,"right":{"value":43,"left":null,"right":{"value":44,"left":null,"right":{"value":47,"left":null,"right":{"value":48,"left":null,"right":{"value":49,"left":null,"right":null}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}
PASS
ok _/home/jkayser/projects/jkayser/gopl/chapter7/exercise7.3 0.001s
[jkayser@oel7latest exercise7.3]$

This is working as intended. There is a difference between deterministic and non-deterministic pseudo-randomness. The sequence of values is pseudo-random, but it is deterministically generated unless you provide a seed:

rand.Seed(time.Now().UnixNano())

Why doesn't it initialized with a seed?

There is value in having deterministic pseudo-randomness and the need for non-determinism is trivially done with the one line shown above.

As you noticed, it also makes it more clear that these are deterministic, which hopefully avoids people mistakingly using them for cryptographic purposes.

Deterministic is a big word. Why not just call it "not random"? So, you can correctly say that the math rand function returns a "not random" (deterministic) number if a seed has not been supplied.

Geeze, throw the poor developer a bone, and add a basic seed initialization call to the package init() function.

func init() {
Seed(time.Now().UnixNano())
}

That would be better than nothing. Or, if you really don't want to do that, have rand return an error if the seed has not been supplied:

if !seeded {
return 0, errors.New("seed not supplied")
}

Why even have a rand function that isn't cryptographically secure? Eliminate it. That way, developers who want randomness will not be able to get unrandomness under any circumstance.

Better yet, have math rand just call crypto rand, and eliminate the caveat about being deterministic.

If the authors of the original code (Alan A. A. Donovan & Brian W. Kernighan) can forget to call the Seed function before calling Rand, what is the likelihood that lesser developers will make the same mistake?

Was this page helpful?
0 / 5 - 0 ratings

Related issues

bbodenmiller picture bbodenmiller  路  3Comments

michaelsafyan picture michaelsafyan  路  3Comments

Miserlou picture Miserlou  路  3Comments

gopherbot picture gopherbot  路  3Comments

natefinch picture natefinch  路  3Comments