From d4a4b26203b0fc19d939eb3c6db479e16b5ef25a Mon Sep 17 00:00:00 2001 From: Bob Nystrom Date: Sun, 7 Feb 2016 10:38:39 -0800 Subject: [PATCH] Add Random.shuffle(). --- doc/site/modules/random/random.markdown | 20 ++++++++++++++++++++ src/optional/wren_opt_random.wren | 12 ++++++++++++ src/optional/wren_opt_random.wren.inc | 12 ++++++++++++ test/random/shuffle.wren | 23 +++++++++++++++++++++++ 4 files changed, 67 insertions(+) create mode 100644 test/random/shuffle.wren diff --git a/doc/site/modules/random/random.markdown b/doc/site/modules/random/random.markdown index db333ddb..7133ab94 100644 --- a/doc/site/modules/random/random.markdown +++ b/doc/site/modules/random/random.markdown @@ -93,3 +93,23 @@ Returns an integer between `start` and `end`, including `start` but excluding System.print(random.int(3, 4)) //> 3 System.print(random.int(-10, 10)) //> -6 System.print(random.int(-4, 2)) //> -2 + +### **shuffle**(list) + +Randomly shuffles the elements in `list`. The items are randomly re-ordered in +place. + + :::wren + var random = Random.new(12345) + var list = (1..5).toList + random.shuffle(list) + System.print(list) //> [3, 2, 4, 1, 5] + +Uses the Fisher-Yates algorithm to ensure that all permutations are chosen +with equal probability. + +Keep in mind that a list with even a modestly large number of elements has an +astronomically large number of permutations. For example, there are about 10^74 +ways a deck of 56 cards can be shuffled. The random number generator's internal +state is not that large, which means there are many permutations it will never +generate. diff --git a/src/optional/wren_opt_random.wren b/src/optional/wren_opt_random.wren index 559200df..17430f58 100644 --- a/src/optional/wren_opt_random.wren +++ b/src/optional/wren_opt_random.wren @@ -46,4 +46,16 @@ foreign class Random { foreign int() int(end) { (float() * end).floor } int(start, end) { (float() * (end - start)).floor + start } + + shuffle(list) { + if (list.isEmpty) return + + // Fisher-Yates shuffle. + for (i in 0...list.count - 1) { + var from = int(i, list.count) + var temp = list[from] + list[from] = list[i] + list[i] = temp + } + } } diff --git a/src/optional/wren_opt_random.wren.inc b/src/optional/wren_opt_random.wren.inc index 7bb61512..d730d3ac 100644 --- a/src/optional/wren_opt_random.wren.inc +++ b/src/optional/wren_opt_random.wren.inc @@ -48,4 +48,16 @@ static const char* randomModuleSource = " foreign int()\n" " int(end) { (float() * end).floor }\n" " int(start, end) { (float() * (end - start)).floor + start }\n" +"\n" +" shuffle(list) {\n" +" if (list.isEmpty) return\n" +"\n" +" // Fisher-Yates shuffle.\n" +" for (i in 0...list.count - 1) {\n" +" var from = int(i, list.count)\n" +" var temp = list[from]\n" +" list[from] = list[i]\n" +" list[i] = temp\n" +" }\n" +" }\n" "}\n"; diff --git a/test/random/shuffle.wren b/test/random/shuffle.wren new file mode 100644 index 00000000..b3f3e58c --- /dev/null +++ b/test/random/shuffle.wren @@ -0,0 +1,23 @@ +import "random" for Random + +var random = Random.new(12345) + +// Empty list. +var list = [] +random.shuffle(list) +System.print(list) // expect: [] + +// One element. +list = [1] +random.shuffle(list) +System.print(list) // expect: [1] + +// Given enough tries, should generate all permutations. +var hits = {} +for (i in 1..200) { + var list = [1, 2, 3, 4] + random.shuffle(list) + hits[list.toString] = true +} + +System.print(hits.count) // expect: 24