Repository: vaskoz/dailycodingproblem-go (go)

Issue:
Day 90
Good morning! Here's your coding interview problem for today.

This question was asked by Google.

Given an integer `n` and a list of integers `l`, write a function that randomly generates a number from `0` to `n-1` that isn't in `l` (uniform).

Pull request description:
Day90
Resolves #189.

Patch:
diff --git a/README.md b/README.md
index 56aaefc..4b4cd17 100644
--- a/README.md
+++ b/README.md
@@ -99,3 +99,4 @@ problems from
 * [Day 87](https://github.com/vaskoz/dailycodingproblem-go/issues/183)
 * [Day 88](https://github.com/vaskoz/dailycodingproblem-go/issues/185)
 * [Day 89](https://github.com/vaskoz/dailycodingproblem-go/issues/187)
+* [Day 90](https://github.com/vaskoz/dailycodingproblem-go/issues/189)
diff --git a/day90/problem.go b/day90/problem.go
new file mode 100644
index 0000000..fb6f868
--- /dev/null
+++ b/day90/problem.go
@@ -0,0 +1,41 @@
+package day90
+
+import (
+	"math/rand"
+	"time"
+)
+
+var random *rand.Rand
+
+// getRandom returns a *math.Rand.
+func getRandom() *rand.Rand {
+	if random == nil {
+		random = rand.New(rand.NewSource(time.Now().UnixNano()))
+	}
+	return random
+}
+
+// RandMissingNumbers randomly generates a number from 0 to n-1 that isn't in l.
+// Runs in O(N) time and O(l) space.
+func RandMissingNumbers(n int, l []int) int {
+	m := make(map[int]struct{})
+	for _, v := range l {
+		m[v] = struct{}{}
+	}
+	size := n - len(m)
+	if size == 0 {
+		return -1
+	}
+	pos := getRandom().Intn(size)
+	val := -1
+	var i int
+	for i = 0; i < n; i++ {
+		if _, found := m[i]; !found {
+			val++
+		}
+		if val == pos {
+			break
+		}
+	}
+	return i
+}


Test patch:
diff --git a/day90/problem_test.go b/day90/problem_test.go
new file mode 100644
index 0000000..fa1b131
--- /dev/null
+++ b/day90/problem_test.go
@@ -0,0 +1,49 @@
+package day90
+
+import "testing"
+
+var testcases = []struct {
+	n            int
+	l            []int
+	distribution float64
+}{
+	{5, []int{0, 1, 2, 2}, 0.5},
+	{5, []int{0, 1, 0, 1}, 0.333},
+	{5, []int{0, 0, 0}, 0.25},
+	{5, []int{0, 1, 2, 3}, 1.0},
+}
+
+func TestRandMissingNumbers(t *testing.T) {
+	//t.Parallel()//don't run in parallel
+	for _, tc := range testcases {
+		results := make(map[int]int)
+		const iterations = 1000
+		for i := 0; i < iterations; i++ {
+			results[RandMissingNumbers(tc.n, tc.l)]++
+		}
+		for _, v := range results {
+			delta := (float64(v) / float64(iterations)) - tc.distribution
+			if delta < 0 {
+				delta = -delta
+			}
+			if delta > 0.1 {
+				t.Errorf("Exceeded 0.1 tolerance for expected %v distribution", tc.distribution)
+			}
+		}
+	}
+}
+
+func TestRandMissingNoNumbers(t *testing.T) {
+	//t.Parallel()//don't run in parallel
+	if result := RandMissingNumbers(5, []int{0, 1, 2, 3, 4}); result != -1 {
+		t.Errorf("Expected -1 for a list that includes all possible numbers")
+	}
+}
+
+func BenchmarkRandMissingNumbers(b *testing.B) {
+	for i := 0; i < b.N; i++ {
+		for _, tc := range testcases {
+			RandMissingNumbers(tc.n, tc.l)
+		}
+	}
+}