diff --git a/vlib/rand/rand.v b/vlib/rand/rand.v index 944776ad79..bf2abc0641 100644 --- a/vlib/rand/rand.v +++ b/vlib/rand/rand.v @@ -467,3 +467,20 @@ pub fn hex(len int) string { pub fn ascii(len int) string { return string_from_set(rand.ascii_chars, len) } + +// shuffle randomly permutates the elements in `a`. +pub fn shuffle(mut a []T) { + len := a.len + for i in 0 .. len { + si := i + intn(len - i) or { len } + a[si], a[i] = a[i], a[si] + } +} + +// shuffle_clone returns a random permutation of the elements in `a`. +// The permutation is done on a fresh clone of `a`, so `a` remains unchanged. +pub fn shuffle_clone(a []T) []T { + mut res := a.clone() + shuffle(mut res) + return res +} diff --git a/vlib/rand/random_numbers_test.v b/vlib/rand/random_numbers_test.v index 0f32adc7c2..06a38cfc2e 100644 --- a/vlib/rand/random_numbers_test.v +++ b/vlib/rand/random_numbers_test.v @@ -317,3 +317,55 @@ fn test_new_global_rng() { rand.set_rng(old) } + +fn test_shuffle() { + mut arrays := [][]int{} + arrays << [0, 1, 2, 3, 4, 5, 6, 7, 8, 9] + arrays << [9, 8, 7, 6, 5, 4, 3, 2, 1, 0] + for seed in seeds { + a := get_n_random_ints(seed, 10) + arrays << a + } + // + mut digits := []map[int]int{len: 10} + for digit in 0 .. 10 { + digits[digit] = {} + for idx in 0 .. 10 { + digits[digit][idx] = 0 + } + } + for mut a in arrays { + o := a.clone() + for _ in 0 .. 100 { + rand.shuffle(mut a) + assert *a != o + for idx in 0 .. 10 { + digits[idx][a[idx]]++ + } + } + } + for digit in 1 .. 10 { + assert digits[0] != digits[digit] + } + for digit in 0 .. 10 { + for idx in 0 .. 10 { + assert digits[digit][idx] > 10 + } + // eprintln('digits[$digit]: ${digits[digit]}') + } +} + +fn test_shuffle_clone() { + original := [0, 1, 2, 3, 4, 5, 6, 7, 8, 9] + mut a := original.clone() + mut results := [][]int{} + for _ in 0 .. 10 { + results << rand.shuffle_clone(a) + } + assert original == a + for idx in 1 .. 10 { + assert results[idx].len == 10 + assert results[idx] != results[0] + assert results[idx] != original + } +}