swiftrandomarc4random

Generate list of unique random numbers in Swift from range


I would like to write a function which should return list of integer. Integer list size and max Random number will be defined by user, which user will pass through function parameter. My special requirement is, integer into list can not be redundant.
My approach using Array:

func getRandomNumbers(maxNumber: Int, listSize: Int)-> [Int]{
    var randomNumbers = [Int]()
    for _ in 1...listSize{
        let randomNumber = Int(arc4random_uniform(UInt32(listSize)))
        randomNumbers.append(randomNumber)
    }
    return randomNumbers
}

Problem: Sometimes i am getting duplicate values with this approach.
I know swift Set doesn't hold duplicate value.

My approach using Set:

func getRandomNumbers(maxNumber: Int, listSize: Int)-> Set<Int>{
    var randomNumbers = Set<Int>()
    for _ in 1...listSize{
        let randomNumber = Int(arc4random_uniform(UInt32(listSize)))
        randomNumbers.insert(randomNumber)
    }
    return randomNumbers
} 

Problem: Sometimes Set size is less then user defined size.


Solution

  • There are 2 problems here:

    1. You don't generate enough numbers. You need to keep generating random numbers until your set is large enough:

      func getRandomNumbers(maxNumber: Int, listSize: Int)-> [Int] {
          var randomNumbers = Set<Int>()
          while randomNumbers.count < listSize {
              let randomNumber = Int(arc4random_uniform(UInt32(maxNumber+1)))
              randomNumbers.insert(randomNumber)
          }
          return randomNumbers
      }
      
    2. You're biasing your random numbers by putting them in the ordering Set chooses, which is highly predictable. You should append your numbers to an array (to keep them in order that they are generated), while still leveraging a set in parallel, for its fast deduplication:

      func getRandomNumbers(maxNumber: Int, listSize: Int)-> [Int] {
          precondition(listSize < maxNumber, "Cannot generate a list of \(listSize) unique numbers, if they all have to be less than \(maxNumber)")
      
          var randomNumbers = (array: [Int](), set: Set<Int>())
      
          while randomNumbers.set.count < listSize {
              let randomNumber = Int(arc4random_uniform(UInt32(maxNumber+1)))
              if randomNumbers.set.insert(randomNumber).inserted { // If the number is unique
                  randomNumbers.array.append(randomNumber) // then also add it to the arary
              }
          }
      
          return randomNumbers.array
      }