0

Hi I am trying to create an array from non-repeating random numbers.

I wrote below code numberOfAnimals which is 10 currently shows how many numbers will be in the array. When I run this in playground I get

"[6, 5, 1, 4, 7, 0]\n" as output of print statement so in total 6 numbers instead of 10. To avoid it I am decreasing value of i in if statement in case random number exists in the array already, in this case for loop needs an additional loop to reach 10 but still it does not work.

Can you please check where is the mistake or give me another code suggestion that can work ?

import UIKit

var array = [Int]()
var max : Int = 10
var numberOfAnimals : Int = 10

for var i in 0..<numberOfAnimals {

    let randomNumber : Int = Int(arc4random_uniform(UInt32(max)))

    if array.contains(randomNumber) {

        i = i - 1

    } else {

        array.append(randomNumber)

    }

}
print(array)
Rob
  • 415,655
  • 72
  • 787
  • 1,044
Dorukk
  • 3
  • 6
  • 1
    It won't work to modify `i` to repeat an iteration of your loop. I suggest you put your numbers into an array and then shuffle the array. See [this question](https://stackoverflow.com/q/24026510/1630618). – vacawama Feb 11 '18 at 19:28
  • Build array, `var array = Array(0 ..< numberOfAnimals)`, and then shuffle it via [Fisher-Yates](https://en.wikipedia.org/wiki/Fisher%E2%80%93Yates_shuffle). – Rob Feb 11 '18 at 20:47

3 Answers3

0

Why not have a while loop that continues until you have ten numbers in your array.

while array.count < 10 {
...

  if !array.contains(randomNumber) {
    array.append(randomNumber)
  }
}
Joakim Danielson
  • 43,251
  • 5
  • 22
  • 52
0

If you repeat your loop until every number has been hit, the worst case runtime of your algorithm is infinite. As far is I understand your problem, you have an array with fixed size and fixed elements, which you want to be shuffled, so why not do this? I found a nice algorithm in another thread:

extension MutableCollection {

    mutating func shuffle() {
        let c = count
        guard c > 1 else { return }
        for (firstUnshuffled, unshuffledCount) in zip(indices, stride(from: c, to: 1, by: -1)) {
            let d: IndexDistance = numericCast(arc4random_uniform(numericCast(unshuffledCount)))
            let i = index(firstUnshuffled, offsetBy: d)
            swapAt(firstUnshuffled, i)
        }
    }

}

let numberOfAnimals = [1,2,3,4,5,6,7,8,9,10]
array.shuffle()
print(array)
sundance
  • 2,930
  • 1
  • 20
  • 25
  • 3
    This is essentially the same approach as in https://stackoverflow.com/a/24026828/1187415, https://stackoverflow.com/a/24045300/1187415 or https://stackoverflow.com/a/47711652/1187415. As pointed out in the comments to those answers, this does not give a uniform distribution, and is also less efficient than the Fisher-Yates shuffle. – Martin R Feb 11 '18 at 20:15
  • Yes, your are right, the approach did not lead to a uniform distribution. I changed my answer and added the algorithm from https://stackoverflow.com/a/24029847/5804550. This algorithm is more efficient and guarantees a uniform distribution. – sundance Feb 11 '18 at 20:29
0

I'd suggest a slight modification of @Joakim Danielson 's answer if you can afford the extra space and implement a set - if an array isn't of vital importance you may want to consider a set for your implementation as well, just $0.02

var array = [Int]()
var seen = Set<Int>()
let max = 10

while array.count < max {

    let number = Int(arc4random_uniform(UInt32(max)))

    if seen.contains(number) {
        continue
    }

    array.append(number)
    seen.insert(number)
}
print(array)
Fred Faust
  • 6,696
  • 4
  • 32
  • 55
  • Like Joakim's answer, this is fine for trivially small sets, but it will become exceedingly inefficient as the set size increases. It's much better to do Fisher-Yates shuffle. – Rob Feb 11 '18 at 20:49