Scala FAQ: Using Scala, how do I generate random numbers without duplicate values, i.e., how do I generate a sequence of random, unique values?
Solution
To show the solution, here’s a Scala 3 function that generates a random sequence of unique integer values:
import scala.util.Random
def generateUniqueRandomNumbers(
count: Int,
minInclusive: Int,
maxExclusive: Int
): Vector[Int] =
require(maxExclusive - minInclusive + 1 >= count, "Range is too small to generate the desired number of unique random numbers")
val random = Random()
var numbers = Set.empty[Int]
while
numbers.size < count
do
numbers += random.nextInt((maxExclusive - minInclusive) + 1) + minInclusive
numbers.toVector
// call the function and use it like this
val uniqueRandomNumbers = generateUniqueRandomNumbers(20, 1, 25)
println(uniqueRandomNumbers)
Keys to the solution
As shown in the code, the keys to this solution are:
- Generate the random values using the Scala
Randomtype. - Initially put the numbers in a
Set; this is because aSetis a type that only contains unique values, which is what we want. - At the end of the function I convert the
Setto aVector, but this is optional. You can just return theSet[Int], if you prefer.
Also:
- The
requirepart of this solution is extremely important. If you don’t include it --- or something similar to it, like your own customException--- you can create an infinite loop.
For example, this use works fine because the count is 5, and the minInclusive and maxExclusive values allow for the creation of five values:
generateUniqueRandomNumbers(5, 1, 5) // result: Vector(5, 1, 2, 3, 4)
But if the count is larger than the range you’re trying to create, you’ll create an infinite loop without the require statement, as shown in the Scala REPL:
scala> generateUniqueRandomNumbers(10, 1, 5)
java.lang.IllegalArgumentException: requirement failed:
Range is too small to generate the desired number of unique random numbers
at scala.Predef$.require(Predef.scala:337)
Handling require’s exception
In idiomatic Scala code — i.e., using Scala best practices — our functions don’t throw exceptions, so wrapping this function in the Try constructor is one way to handle this situation:
import scala.util.{Random, Try}
def generateUniqueRandomNumbers(
count: Int,
minInclusive: Int,
maxExclusive: Int
): Try[Vector[Int]] = Try { //<-- i added Try here
require(maxExclusive - minInclusive + 1 >= count, "Range is too small to generate the desired number of unique random numbers")
val random = Random()
var numbers = Set.empty[Int]
while
numbers.size < count
do
numbers += random.nextInt((maxExclusive - minInclusive) + 1) + minInclusive
numbers.toVector
}
The Scala REPL shows how this works:
scala> generateUniqueRandomNumbers(5, 1, 10)
val res0: util.Try[Vector[Int]] = Success(Vector(1, 6, 2, 7, 3))
As you can see in the res0 value, this is the result of that function call:
Success(Vector(1, 6, 2, 7, 3))
Because we now have a Vector wrapped inside a Success value, you’ll want to handle that Success value with something like a match expression:
res0 match
case Success(seq) => println(seq)
case Failure(err) => System.err.println(err)
| this post is sponsored by my books: | |||
#1 New Release |
FP Best Seller |
Learn Scala 3 |
Learn FP Fast |
Related information
If you don’t know anything about handling values like Try, Option, or Either with match expressions, see these links: