Let's say I want to generate a random number between 1 and 100, but I don't want to include 42. How would I do this without repeating the random method until it is not 42.
int i = rand(1, 9); if i>=7 i++; return i; As long as you ensure that your mapping is 1:1, you can avoid skewing the randomness of your rand function. Ax. The other way round would be better: create numbers from1 to 8 and map 7 and 8 to 8 and 9.
Updated for Swift 5.1
var nums = [Int](1...100)
nums.remove(at: 42)
let random = Int(arc4random_uniform(UInt32(nums.count)))
print(nums[random])
This extension of Range
does provide a solution when you want to exclude more than 1 value.
extension ClosedRange where Element: Hashable {
func random(without excluded:[Element]) -> Element {
let valid = Set(self).subtracting(Set(excluded))
let random = Int(arc4random_uniform(UInt32(valid.count)))
return Array(valid)[random]
}
}
Example
(1...100).random(without: [40,50,60])
I believe the computation complexity of this second solution is O(n)
where n is the number of elements included in the range.
The assumption here is the no more than n excluded values are provided by the caller.
appzYourLife has some great general purpose solutions, but I want to tackle the specific problem in a lightweight way.
Both of these approaches work roughly the same way: Narrow the range to the random number generator to remove the impossible answer (99 answers instead of 100), then map the result so it isn't the illegal value.
Neither approach increases the probability of an outcome relative to another outcome. That is, assuming your random number function is perfectly random the result will still be random (and no 2x chance of 43 relative to 5, for instance).
Get a random number from 1 to 99. If it's greater than or equal to the number you want to avoid, add one to it.
func approach1()->Int {
var number = Int(arc4random_uniform(99)+1)
if number >= 42 {
number = number + 1
}
return number
}
As an example, trying to generate a random number from 1-5 that's not 3, take a random number from 1 to 4 and add one if it's greater than or equal to 3.
Another simple way would be to get a number from 1 to 99. If it's exactly equal to the number you're trying to avoid, make it 100 instead.
func approach2()->Int {
var number = Int(arc4random_uniform(99)+1)
if number == 42 {
number = 100
}
return number
}
Using this algorithm and narrowing the range to 1-5 (while avoiding 3) again, we get these possible outcomes:
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With