Generate a Swift array of nonrepeating random numb

2020-04-11 11:01发布

问题:

I'd like to generate multiple different random numbers in Swift. Here is the procedure.

  1. Set up an empty array
  2. Generate a random number
  3. Check if the array is empty
    a. If the array is empty, insert the random number
    b. If the array is not empty, compare the random number to the numbers in array
    i. If the numbers are the same, repeat 2
    ii. if the numbers are not the same, insert the random number and repeat 2

    import UIKit 
    
    //the random number generator
    func randomInt(min: Int, max:Int) -> Int {
        return min + Int(arc4random_uniform(UInt32(max - min + 1)))
    }
    
    var temp = [Int]()
    for var i = 0; i<4; i++ {
      var randomNumber = randomInt(1, 5)
      if temp.isEmpty{
        temp.append(randomNumber)
      } else {
      //I don't know how to continue...
     }
    }
    

回答1:

If you use your method the problem is, that you will create a new random-number each time. So you possibly could have the same random-number 4 times and so your array will only have one element.

So, if you just want to have an array of numbers from within a specific range of numbers (for example 0-100), in a random order, you can first fill an array with numbers in 'normal' order. For example with for loop etc:

var min = 1
var max = 5
for var i = min; i<= max; i++ {
    temp.append(i)
}

After that, you can use a shuffle method to shuffle all elements of the array with the shuffle method from this answer:

func shuffle<C: MutableCollectionType where C.Index == Int>(var list: C) -> C {
    let count = countElements(list)
    for i in 0..<(count - 1) {
        let j = Int(arc4random_uniform(UInt32(count - i))) + i
        swap(&list[i], &list[j])
    }
    return list
}

Ater that you can do something like that:

shuffle(temp)        // e.g., [3, 1, 2, 4, 5]


回答2:

The construct you’re looking for with your approach might be something like:

var temp: [Int] = []
while temp.count < 4 {
    var randomNumber: Int
    do {
        randomNumber = randomInt(1, 5)
    } while contains(temp, randomNumber)
    temp.append(randomNumber)
}

This will be fine for tiny ranges like yours, but for larger ranges it will be very slow, because for the last few numbers you are waiting for the random number to hit precisely the remaining handful of possibilities. I just tried generating from a range of 200 in a playground and it took 9 seconds.

If you want a random selection of numbers with guaranteed coverage over a range, you could generate it like by taking that range and shuffling it, like this:

func shuffle<S: SequenceType>(source: S) -> [S.Generator.Element] {
    var rangen = GeneratorOf { arc4random() }
    let a = Array(Zip2(rangen, source))
    return a.sorted { $0.0 < $1.0 }.map { $0.1 }
}

let min = 1, max = 5
shuffle(min...max)

If you want a selection of n non-repeating random numbers from a range 0..<m, there’s a particularly pretty algorithm to do this that generates an ascending sequence of random numbers from that range:

func randomGeneratorOf(#n: Int, #from: Int) -> GeneratorOf<Int> {

    var select = UInt32(n)
    var remaining = UInt32(from)
    var i = 0

    return GeneratorOf {
        while i < from {
            if arc4random_uniform(remaining) < select {
                --select
                --remaining
                return i++
            }
            else {
                --remaining
                ++i
            }
        }
        return nil
    }
}

Which you could use like so:

let engines = [
    "Duck","Emily","Gordon","Henry", "Mavis",
    "Belle","James","Edward","Thomas","Toby"
]

let picks = Array(randomGeneratorOf(n: 3, from: engines.count))

for engine in PermutationGenerator(elements: engines, indices: picks) {
    println(engine)
}


回答3:

Below is my suggestion. I like this way since it is short and simple :)

let totalCount: Int = 150 //Any number you asssign
var randomNumArray: [Int] = []
var i = 0
while randomNumArray.count < totalCount {
    i++
    let rand = Int(arc4random_uniform(UInt32(totalCount)))
    for(var ii = 0; ii < totalCount; ii++){
        if randomNumArray.contains(rand){
            print("do nothing")
        } else {
            randomNumArray.append(rand)
        }
    }
}