12
 scala> Random.shuffle((1 to 10).toSet)
 res10: scala.collection.immutable.Set[Int] = Set(5, 10, 1, 6, 9, 2, 7, 3, 8, 4)

 scala> Random.shuffle((1 to 10).toSet)
 res11: scala.collection.immutable.Set[Int] = Set(5, 10, 1, 6, 9, 2, 7, 3, 8, 4)

 scala> Random.shuffle((1 to 10).toSet)
 res12: scala.collection.immutable.Set[Int] = Set(5, 10, 1, 6, 9, 2, 7, 3, 8, 4)

 scala> Random.shuffle((1 to 10).toList)
 res13: List[Int] = List(3, 9, 8, 5, 7, 6, 10, 2, 1, 4)

 scala> Random.shuffle((1 to 10).toList)
 res14: List[Int] = List(5, 10, 2, 9, 4, 7, 8, 6, 1, 3)

 scala> Random.shuffle((1 to 10).toList)
 res15: List[Int] = List(5, 9, 10, 6, 8, 3, 4, 1, 7, 2)

So shuffle can handle Lists just fine, but not sets ? Can't sets be shuffled ? Why is res10 == res11 == res12 ?

k r
  • 409
  • 2
  • 6
  • 13
  • Your complaint is not about the behavior of Random, but rather the behavior of Set. The Set contract does not imply preservation of order -- conveniently allowing for optimizations such as backing the Set with a hashtable -- thus calling toSet is an invitation to re-order your collection. – Matthew Mark Miller May 16 '16 at 14:03

2 Answers2

21

Scala's sets aren't ordered (just like the mathematical ones). They are iterable, however—you just can't rely on the order that you'll get the items in. Many implementations of sets will iterate the same elements in the same order—i.e.,

scala> Set(1, 2, 3, 4, 5).toList == Set(5, 4, 3, 2, 1).toList
res0: Boolean = true

Which explains the effect you're seeing here. You should never rely on this, though—there could be a perfect valid Set implementation for which the above wouldn't hold.

Travis Brown
  • 138,631
  • 12
  • 375
  • 680
  • 1
    Uh...ok. I accept your explanation, though I don't think Random.shuffle is fulfilling the postcondition here. The shuffle algorithm must provide a random order regardless of the underlying container. Its very easy to provide this behavior. For example: Random.shuffle((1 to 10).toSet) can internally call Random.shuffle((1 to 10).toSet.toList) to get an arbitrary ordering, which can then be shuffled. – k r Jun 28 '12 at 20:58
  • 7
    It is a little weird, and I'm not sure why `shuffle` is defined for any `TraversableOnce` (instead of say `Seq`). It's simply not possible to write a version of `shuffle` for sets that would provide a random order, since it doesn't make sense to talk about _any_ kind of order for sets. – Travis Brown Jun 28 '12 at 21:02
  • 2
    Just saw your comment edit: yes, but `shuffle` returns a collection of the same type as its input, and as soon as you put the shuffled list back into a set you're stuck in the same situation. – Travis Brown Jun 28 '12 at 21:04
  • And please don't accept my explanation if it's not clear to you! – Travis Brown Jun 28 '12 at 21:06
  • Order does not matter to Set equality: the elements are either present in both sets or one set has one or more element that the other doesn't have. You can think of Set equality as two substractions: `(a -- b).isEmpty && (b -- a).isEmpty`. If this is true, then boths sets have exactly the same elements. – François Beausoleil Mar 25 '16 at 12:40
2

Random is "shuffling" the Set; it just has no visible effect since sets do not have an order. The REPL happens to print the shuffled sets the same way every time:

scala> Set(1,2,3,4,5) 
res29: scala.collection.immutable.Set[Int] = Set(5, 1, 2, 3, 4)

scala> Set(5,4,3,2,1)
res30: scala.collection.immutable.Set[Int] = Set(5, 1, 2, 3, 4)

scala> util.Random.shuffle(res30)
res31: scala.collection.immutable.Set[Int] = Set(5, 1, 2, 3, 4)
retrospectacus
  • 2,518
  • 1
  • 11
  • 18