22

The method I've devised so far is this:

func randRange (lower : Int , upper : Int) -> Int {
    let difference = upper - lower
    return Int(Float(rand())/Float(RAND_MAX) * Float(difference + 1)) + lower
}

This generates random integers between lower and upper inclusive.

masoud
  • 55,379
  • 16
  • 141
  • 208
RonH
  • 504
  • 1
  • 3
  • 12
  • That looks reasonably easy to me. What is the problem? – Luke Jun 05 '14 at 11:11
  • 2
    Luke, I just wondered if there was some functionality either within or outside of Swift that I was missing and could incorporate in order to make it either more compact or more reliable. I think the answers below make clear that there is. – RonH Jun 06 '14 at 13:07
  • Fair enough. I've not really had a chance to properly get to grips with it, yet. – Luke Jun 06 '14 at 19:56
  • 1
    Isn't it pretty broken not to have this built-in? – Alper Aug 10 '15 at 14:51
  • `let random = arc4random_uniform(max - min + 1) + min`, for instance, where the _range_ has a `min` and a `max` value (both `Uint32`) – feel free to extend to concept for random negative numbers (optionally). – holex Jan 22 '18 at 15:55

4 Answers4

51

Here's a somewhat lighter version of it:

func randRange (lower: Int , upper: Int) -> Int {
    return lower + Int(arc4random_uniform(UInt32(upper - lower + 1)))
}

This can be simplified even further if you decide this function works with unsigned values only:

func randRange (lower: UInt32 , upper: UInt32) -> UInt32 {
    return lower + arc4random_uniform(upper - lower + 1)
}

Or, following Anton's (+1 for you) excellent idea of using a range as parameter:

func random(range: Range<UInt32>) -> UInt32 {
    return range.startIndex + arc4random_uniform(range.endIndex - range.startIndex + 1)
}
Jean Le Moignan
  • 22,158
  • 3
  • 31
  • 37
14

Edited to remove modulo bias per the suggestion in comments. (thanks!)

I think a neat way of doing this may be to use Swift's Range to define the bounds because then you can specify 1..100 or 1...100 (including or excluding the upper bound). The best I have come up with so far is:

import Foundation // needed for rand()

func randInRange(range: Range<Int>) -> Int {
    // arc4random_uniform(_: UInt32) returns UInt32, so it needs explicit type conversion to Int
    // note that the random number is unsigned so we don't have to worry that the modulo
    // operation can have a negative output
    return  Int(arc4random_uniform(UInt32(range.endIndex - range.startIndex))) + range.startIndex
}

// generate 10 random numbers between -1000 and 999
for _ in 0...100 {
    randInRange(-1000...1000)
}

I tried using an extension on Range but you cannot seem to extend Range< T where T: Int > specifically. It would be even better if you could get a syntax like (1..100).rand().

Leo Dabus
  • 229,809
  • 59
  • 489
  • 571
Anton Tcholakov
  • 3,710
  • 1
  • 12
  • 10
  • 2
    [Modulo bias](http://stackoverflow.com/questions/10984974/why-do-people-say-there-is-modulo-bias-when-using-a-random-number-generator) is a well-known result: using modulo arithmetic yields non-uniform results in most cases. – pjs Jun 05 '14 at 16:01
  • Thanks, Anton - it is nice to work with the range functions - however, I do prefer the visual simplicity of Jean's solution. – RonH Jun 06 '14 at 13:18
  • Currently (on Xcode 6 beta 5) this won't generate a range between -1000 and 999 as the comment says, rather it will generate one between -1000 and 1000. The value of range.endIndex corresponds to the value of a ..< range bound (i.e. the exclusive bound). Because of this the +1 is unnecessary. Jean's version above obviously also has the same problem. What I don't get is why they would use the exclusive bound, seems counterintuitive for a property called endIndex...maybe this has changed in beta 6? – iOS Gamer Aug 27 '14 at 23:42
6

This can be done in the latest Swift version:

Int.random(in: 1...99)

The above will generate a random integer between 1 and 99 (inclusive of 99).

Cristik
  • 30,989
  • 25
  • 91
  • 127
Bilal Saleem
  • 633
  • 4
  • 8
2

If you are into extensions:

extension CountableClosedRange where Bound == Int {
    var randomValue: Int {
        return lowerBound + Int(arc4random_uniform(UInt32(upperBound - lowerBound)))
    }
}

extension CountableRange where Bound == Int {
    var randomValue: Int {
        return lowerBound + Int(arc4random_uniform(UInt32(upperBound - lowerBound)))
    }
}

(0...6).randomValue
(0..<7).randomValue
lucamegh
  • 527
  • 6
  • 16