Skip to content

Commit 5a59bf0

Browse files
bpsmstuarthalloway
authored andcommitted
Replace hackish generators/shuffle with a fisher-yates implementation
This: (sort (fn [_] (long)) coll) Does not work on JDK7 since TimSort is clever enough to detect that the ordering function is behaving inconsistently, resulting in: java.lang.IllegalArgumentException: Comparison method violates its general contract! Also, this is just a bad idea: A variant of the above method that has seen some use in languages that support sorting with user-specified comparison functions is to shuffle a list by sorting it with a comparison function that returns random values. However, this is an extremely bad method: it is very likely to produce highly non-uniform distributions, which in addition depends heavily on the sorting algorithm used. http://en.wikipedia.org/wiki/Fisher–Yates_shuffle#Comparison_with_other_shuffling_algorithms We can't use clojure.core/shuffle since we want whatever shuffling we do to be repeatable given the same input and same initial state of the random number generator. This patch provides an implementation of the fisher-yates shuffle and then replaces the body of shuffle with a call to said function. Signed-off-by: Stuart Halloway <stu@thinkrelevance.com>
1 parent a73c11e commit 5a59bf0

1 file changed

Lines changed: 16 additions & 1 deletion

File tree

src/main/clojure/clojure/test/generative/generators.clj

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -274,10 +274,25 @@ instance you can get a repeatable basis for tests."
274274
[]
275275
(one-of scalar collection))
276276

277+
(defn ^:private fisher-yates
278+
"http://en.wikipedia.org/wiki/Fisher–Yates_shuffle#The_modern_algorithm"
279+
[coll]
280+
(let [as (object-array coll)]
281+
(loop [i (dec (count as))]
282+
(if (<= 1 i)
283+
(let [j (uniform 0 (inc i))
284+
t (aget as i)]
285+
(aset as i (aget as j))
286+
(aset as j t)
287+
(recur (dec i)))
288+
(into (empty coll) (seq as))))))
289+
277290
(defn shuffle
278291
"Shuffle coll"
279292
[coll]
280-
(sort-by (fn [_] (long)) coll))
293+
;; using our own fischer-yates instead of core/shuffle so that
294+
;; we'll get the same shuffle, given the same random *seed*.
295+
(fisher-yates coll))
281296

282297

283298

0 commit comments

Comments
 (0)