Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How do I generate a random number not including one without using a while loop?

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.

like image 538
dhruvm Avatar asked Dec 17 '15 20:12

dhruvm


People also ask

How can I generate a random number within a range but exclude some?

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.


2 Answers

Updated for Swift 5.1

Excluding 1 value

var nums = [Int](1...100)
nums.remove(at: 42)

let random = Int(arc4random_uniform(UInt32(nums.count)))
print(nums[random])

Excluding multiple values

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.

like image 95
Luca Angeletti Avatar answered Oct 06 '22 02:10

Luca Angeletti


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).

Approach 1: Addition.

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.

  • rand(1..4) produces 1, +0, = 1
  • rand(1..4) produces 2, +0, = 2
  • rand(1..4) produces 3, +1, = 4
  • rand(1..4) produces 4, +1, = 5

Approach 2: Avoidance.

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:

  • rand(1..4) produces 1; allowed, so Result = 1
  • rand(1..4) produces 2, allowed, so Result = 2
  • rand(1..4) produces 3; not allowed, so Result = 5
  • rand(1..4) produces 4, allowed, so Result = 4
like image 31
Steven Fisher Avatar answered Oct 06 '22 01:10

Steven Fisher