Navigation Menu

Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

math: rand: not random. #21771

Closed
jeffkayser2 opened this issue Sep 5, 2017 · 10 comments
Closed

math: rand: not random. #21771

jeffkayser2 opened this issue Sep 5, 2017 · 10 comments

Comments

@jeffkayser2
Copy link

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.

@jeffkayser2
Copy link
Author

[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]$

@jeffkayser2
Copy link
Author

[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]$

@jeffkayser2
Copy link
Author

[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]$

@dsnet
Copy link
Member

dsnet commented Sep 5, 2017

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())

@dsnet dsnet closed this as completed Sep 5, 2017
@jeffkayser2
Copy link
Author

Why doesn't it initialized with a seed?

@dsnet
Copy link
Member

dsnet commented Sep 5, 2017

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

@dsnet
Copy link
Member

dsnet commented Sep 5, 2017

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

@jeffkayser2
Copy link
Author

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.

@jeffkayser2
Copy link
Author

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

@jeffkayser2
Copy link
Author

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?

@golang golang locked and limited conversation to collaborators Sep 5, 2018
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Projects
None yet
Development

No branches or pull requests

3 participants